Fixed a lot of internationalisation issues.
[pspp-builds.git] / src / ui / gui / var-sheet.c
1 /* 
2    PSPPIRE --- A Graphical User Interface for PSPP
3    Copyright (C) 2004, 2005, 2006  Free Software Foundation
4    Written by John Darrington
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19    02110-1301, USA. */
20
21
22 /* This module creates the Variable Sheet used for inputing the
23    variables in the  dictonary */
24
25 #include <config.h>
26 #include <gettext.h>
27 #define _(msgid) gettext (msgid)
28 #define N_(msgid) msgid
29
30 #include <data/value-labels.h>
31
32 #include <glade/glade.h>
33 #include <gtk/gtk.h>
34
35 #include <stdlib.h>
36 #include <string.h>
37
38 #include <data/value.h>
39
40 #include <gtksheet/gtksheet.h>
41 #include <gtksheet/gsheet-hetero-column.h>
42 #include <gtksheet/gsheet-uniform-row.h>
43
44 #include "psppire-var-store.h"
45 #include "helper.h"
46 #include "menu-actions.h"
47 #include "psppire-dict.h"
48 #include "psppire-variable.h"
49 #include "var-type-dialog.h"
50 #include "var-sheet.h"
51 #include "customentry.h"
52
53 #include "val-labs-dialog.h"
54 #include "missing-val-dialog.h"
55
56
57
58 static const gint n_initial_rows = 40;
59
60
61 extern GladeXML *xml;
62
63 struct column_parameters
64 {
65   gchar label[20];  
66   gint width ;
67 };
68
69 static const struct column_parameters column_def[] = {
70   { N_("Name"),    80},
71   { N_("Type"),    100},
72   { N_("Width"),   57}, 
73   { N_("Decimals"),91}, 
74   { N_("Label"),   95}, 
75   { N_("Values"),  103},
76   { N_("Missing"), 95}, 
77   { N_("Columns"), 80}, 
78   { N_("Align"),   69}, 
79   { N_("Measure"), 99}, 
80 };
81
82
83 static gboolean
84 click2row(GtkWidget *w, gint row, gpointer data)
85 {
86   gint current_row, current_column;
87   GtkWidget *data_sheet  = get_widget_assert(xml, "data_sheet");
88
89   select_sheet(PAGE_DATA_SHEET);
90
91   gtk_sheet_get_active_cell(GTK_SHEET(data_sheet), 
92                             &current_row, &current_column);
93
94   gtk_sheet_set_active_cell(GTK_SHEET(data_sheet), current_row, row);
95
96   return FALSE;
97 }
98
99
100
101 const gchar *alignments[]={
102   N_("Left"),
103   N_("Right"),
104   N_("Centre"),
105   0
106 };
107
108 const gchar *measures[]={
109   N_("Nominal"),
110   N_("Ordinal"),
111   N_("Scale"),
112   0
113 };
114
115 static GtkListStore *
116 create_label_list(const gchar **labels)
117 {
118   const gchar *s;
119   gint i = 0;
120   GtkTreeIter iter;
121
122   GtkListStore *list_store;
123   list_store = gtk_list_store_new (1, G_TYPE_STRING);
124
125
126   while ( (s = labels[i++]))
127     {
128       gtk_list_store_append (list_store, &iter);
129       gtk_list_store_set (list_store, &iter,
130                           0, gettext(s),
131                           -1);
132     }
133         
134   return list_store;
135 }
136
137 /* Callback for when the alignment combo box 
138    item is selected */
139 static void        
140 change_alignment(GtkComboBox *cb,
141     gpointer user_data)
142 {
143   struct PsppireVariable *pv = user_data;
144   gint active_item = gtk_combo_box_get_active(cb);
145
146   if ( active_item < 0 ) return ;
147
148   psppire_variable_set_alignment(pv, active_item);
149 }
150
151
152
153 /* Callback for when the measure combo box 
154    item is selected */
155 static void        
156 change_measure(GtkComboBox *cb,
157     gpointer user_data)
158 {
159   struct PsppireVariable *pv = user_data;
160   gint active_item = gtk_combo_box_get_active(cb);
161
162   if ( active_item < 0 ) return ;
163
164   psppire_variable_set_measure(pv, active_item);
165 }
166
167
168
169 static gboolean 
170 traverse_cell_callback (GtkSheet * sheet, 
171                         gint row, gint column, 
172                         gint *new_row, gint *new_column
173                         )
174 {
175   PsppireVarStore *var_store = PSPPIRE_VAR_STORE(gtk_sheet_get_model(sheet));
176
177   gint n_vars = psppire_var_store_get_var_cnt(var_store);
178
179   if ( row == n_vars && *new_row >= n_vars)
180     {
181       GtkEntry *entry = GTK_ENTRY(gtk_sheet_get_entry(sheet));
182
183       const gchar *name = gtk_entry_get_text(entry);
184
185       if (! psppire_dict_check_name(var_store->dict, name, TRUE))
186         return FALSE;
187       
188       psppire_dict_insert_variable(var_store->dict, row, name);
189
190       return TRUE;
191     }
192
193   /* If the destination cell is outside the current  variables, then
194      accept the destination only as the name column of the first blank row
195   */
196   if ( *new_row > n_vars) 
197     return FALSE;
198   
199   if ( *new_row >= n_vars && *new_column != COL_NAME) 
200     return FALSE;
201
202   return TRUE;
203 }
204
205
206 /* Callback whenever the cell on the var sheet is entered or left.
207    It sets the entry box type appropriately.
208 */
209 static gboolean 
210 var_sheet_cell_change_entry (GtkSheet * sheet, gint row, gint column, 
211                              gpointer leaving)
212 {
213   GtkSheetCellAttr attributes;
214   PsppireVarStore *var_store ;
215   struct PsppireVariable *pv ;
216
217   g_return_val_if_fail(sheet != NULL, FALSE);
218
219   var_store = PSPPIRE_VAR_STORE(gtk_sheet_get_model(sheet));
220
221   if ( row >= psppire_var_store_get_var_cnt(var_store))
222     return TRUE;
223
224   if ( leaving ) 
225     {
226       gtk_sheet_change_entry(sheet, GTK_TYPE_ENTRY);
227       return TRUE;
228     }
229
230
231   gtk_sheet_get_attributes(sheet, row, column, &attributes);
232
233   pv = psppire_var_store_get_variable(var_store, row);
234
235   switch (column)
236     {
237     case COL_ALIGN:
238       {
239         static GtkListStore *list_store = 0;
240         GtkComboBoxEntry *cbe;
241         gtk_sheet_change_entry(sheet, GTK_TYPE_COMBO_BOX_ENTRY);
242         cbe = 
243           GTK_COMBO_BOX_ENTRY(gtk_sheet_get_entry(sheet)->parent);
244
245
246         if ( ! list_store) list_store = create_label_list(alignments);
247
248         gtk_combo_box_set_model(GTK_COMBO_BOX(cbe), 
249                                 GTK_TREE_MODEL(list_store));
250
251         gtk_combo_box_entry_set_text_column (cbe, 0);
252
253
254         g_signal_connect(G_OBJECT(cbe),"changed", 
255                          G_CALLBACK(change_alignment), pv);
256       }
257       break;
258     case COL_MEASURE:
259       {
260         static GtkListStore *list_store = 0;
261         GtkComboBoxEntry *cbe;
262         gtk_sheet_change_entry(sheet, GTK_TYPE_COMBO_BOX_ENTRY);
263         cbe = 
264           GTK_COMBO_BOX_ENTRY(gtk_sheet_get_entry(sheet)->parent);
265
266
267         if ( ! list_store) list_store = create_label_list(measures);
268
269         gtk_combo_box_set_model(GTK_COMBO_BOX(cbe), 
270                                 GTK_TREE_MODEL(list_store));
271
272         gtk_combo_box_entry_set_text_column (cbe, 0);
273
274         g_signal_connect(G_OBJECT(cbe),"changed", 
275                          G_CALLBACK(change_measure), pv);
276       }
277       break;
278
279     case COL_VALUES:
280       {
281         static struct val_labs_dialog *val_labs_dialog = 0;
282
283         PsppireCustomEntry *customEntry;
284
285         gtk_sheet_change_entry(sheet, PSPPIRE_CUSTOM_ENTRY_TYPE);
286
287         customEntry = 
288           PSPPIRE_CUSTOM_ENTRY(gtk_sheet_get_entry(sheet));
289
290
291         if (!val_labs_dialog ) 
292             val_labs_dialog = val_labs_dialog_create(xml);
293
294         val_labs_dialog->pv = pv;
295
296         g_signal_connect_swapped(GTK_OBJECT(customEntry),
297                                  "clicked",
298                                  GTK_SIGNAL_FUNC(val_labs_dialog_show),
299                                  val_labs_dialog);
300       }
301       break;
302     case COL_MISSING:
303       {
304         static struct missing_val_dialog *missing_val_dialog = 0;
305         PsppireCustomEntry *customEntry;
306         
307         gtk_sheet_change_entry(sheet, PSPPIRE_CUSTOM_ENTRY_TYPE);
308
309         customEntry = 
310           PSPPIRE_CUSTOM_ENTRY(gtk_sheet_get_entry(sheet));
311
312         if (!missing_val_dialog ) 
313             missing_val_dialog = missing_val_dialog_create(xml);
314
315         missing_val_dialog->pv = psppire_var_store_get_variable(var_store, row);
316
317         g_signal_connect_swapped(GTK_OBJECT(customEntry),
318                                  "clicked",
319                                  GTK_SIGNAL_FUNC(missing_val_dialog_show),
320                                  missing_val_dialog);
321       }
322       break;
323
324     case COL_TYPE:
325       {
326         static struct var_type_dialog *var_type_dialog = 0;
327
328         PsppireCustomEntry *customEntry;
329
330         gtk_sheet_change_entry(sheet, PSPPIRE_CUSTOM_ENTRY_TYPE);
331
332         customEntry = 
333           PSPPIRE_CUSTOM_ENTRY(gtk_sheet_get_entry(sheet));
334
335
336         /* Popup the Variable Type dialog box */
337         if (!var_type_dialog ) 
338             var_type_dialog = var_type_dialog_create(xml);
339
340
341         var_type_dialog->pv = pv;
342
343         g_signal_connect_swapped(GTK_OBJECT(customEntry),
344                                  "clicked",
345                                  GTK_SIGNAL_FUNC(var_type_dialog_show),
346                                  var_type_dialog);
347       }
348       break;
349     case COL_WIDTH:
350     case COL_DECIMALS:
351     case COL_COLUMNS:
352       {
353         if ( attributes.is_editable) 
354           {
355             gint r_min, r_max;
356
357             const gchar *s = gtk_sheet_cell_get_text(sheet, row, column);
358
359             if (!s) 
360               return FALSE;
361
362             {
363               GtkSpinButton *spinButton ;
364               const gint current_value  = atoi(s);
365               GtkObject *adj ;
366
367               const struct fmt_spec *fmt = psppire_variable_get_write_spec(pv);
368               switch (column) 
369                 {
370                 case COL_WIDTH:
371                   r_min = fmt->d + 1;
372                   r_max = (psppire_variable_get_type(pv) == ALPHA) ? MAX_STRING : 40;
373                   break;
374                 case COL_DECIMALS:
375                   r_min = 0 ; 
376                   r_max = MIN(fmt->w - 1, 16);
377                   break;
378                 case COL_COLUMNS:
379                   r_min = 1;
380                   r_max = 255 ; /* Is this a sensible value ? */
381                   break;
382                 default:
383                   g_assert_not_reached();
384                 }
385
386               adj = gtk_adjustment_new(current_value,
387                                        r_min, r_max,
388                                        1.0, 1.0, 1.0 /* steps */
389                                        );
390
391               gtk_sheet_change_entry(sheet, GTK_TYPE_SPIN_BUTTON);
392
393               spinButton = 
394                 GTK_SPIN_BUTTON(gtk_sheet_get_entry(sheet));
395
396               gtk_spin_button_set_adjustment(spinButton, GTK_ADJUSTMENT(adj));
397               gtk_spin_button_set_digits(spinButton, 0);
398             }
399           }
400       }
401       break; 
402
403     default:
404       gtk_sheet_change_entry(sheet, GTK_TYPE_ENTRY);
405       break;
406     }
407
408   return TRUE;
409 }
410
411
412
413 /* Create the var sheet */
414 GtkWidget*
415 psppire_variable_sheet_create (gchar *widget_name, 
416                                gchar *string1, 
417                                gchar *string2,
418                                gint int1, gint int2)
419 {
420   gint i;
421   GtkWidget *sheet;
422
423   GObject *geo = g_sheet_hetero_column_new(75, n_COLS);
424   GObject *row_geometry = g_sheet_uniform_row_new(25, n_initial_rows); 
425
426
427
428   sheet = gtk_sheet_new(G_SHEET_ROW(row_geometry),
429                         G_SHEET_COLUMN(geo), 
430                         "variable sheet", 0); 
431
432   g_signal_connect (GTK_OBJECT (sheet), "activate",
433                     GTK_SIGNAL_FUNC (var_sheet_cell_change_entry),
434                     0);
435
436   g_signal_connect (GTK_OBJECT (sheet), "deactivate",
437                     GTK_SIGNAL_FUNC (var_sheet_cell_change_entry),
438                     (void *) 1);
439
440   g_signal_connect (GTK_OBJECT (sheet), "traverse",
441                     GTK_SIGNAL_FUNC (traverse_cell_callback), 0);
442
443
444   g_signal_connect (GTK_OBJECT (sheet), "double-click-row",
445                     GTK_SIGNAL_FUNC (click2row),
446                     sheet);
447
448   for (i = 0 ; i < n_COLS ; ++i ) 
449     {
450       g_sheet_hetero_column_set_button_label(G_SHEET_HETERO_COLUMN(geo), i, 
451                                                gettext(column_def[i].label));
452
453       g_sheet_hetero_column_set_width(G_SHEET_HETERO_COLUMN(geo), i, 
454                                                column_def[i].width);
455     }
456
457   gtk_widget_show(sheet);
458
459   return sheet;
460 }
461
462