d8e6f6f7195e0a15dd1bd7ddbf961278b4afedbb
[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
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
21 /* This module creates the Variable Sheet used for inputing the
22    variables in the  dictonary */
23
24 #include <config.h>
25 #include <gettext.h>
26 #define _(msgid) gettext (msgid)
27 #define N_(msgid) msgid
28
29 #include <data/value-labels.h>
30
31 #include <glade/glade.h>
32 #include <gtk/gtk.h>
33
34 #include <stdlib.h>
35 #include <string.h>
36 #if HAVE_LANGINFO_H
37 #include <langinfo.h>
38 #endif
39
40 #include <data/value.h>
41
42 #include <gtksheet/gtksheet.h>
43 #include <gtksheet/gsheet-hetero-column.h>
44 #include <gtksheet/gsheet-uniform-row.h>
45
46 #include "psppire-var-store.h"
47 #include "helper.h"
48 #include "psppire-dict.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
62 struct column_parameters
63 {
64   gchar label[20];
65   gint width ;
66 };
67
68 static const struct column_parameters column_def[] = {
69   { N_("Name"),    80},
70   { N_("Type"),    100},
71   { N_("Width"),   57},
72   { N_("Decimals"),91},
73   { N_("Label"),   95},
74   { N_("Values"),  103},
75   { N_("Missing"), 95},
76   { N_("Columns"), 80},
77   { N_("Align"),   69},
78   { N_("Measure"), 99},
79 };
80
81
82
83 const gchar *const alignments[n_ALIGNMENTS + 1]={
84   N_("Left"),
85   N_("Right"),
86   N_("Centre"),
87   0
88 };
89
90 const gchar *const measures[n_MEASURES + 1]={
91   N_("Nominal"),
92   N_("Ordinal"),
93   N_("Scale"),
94   0
95 };
96
97 static GtkListStore *
98 create_label_list (const gchar *const *labels)
99 {
100   const gchar *s;
101   gint i = 0;
102   GtkTreeIter iter;
103
104   GtkListStore *list_store;
105   list_store = gtk_list_store_new (1, G_TYPE_STRING);
106
107
108   while ( (s = labels[i++]))
109     {
110       gtk_list_store_append (list_store, &iter);
111       gtk_list_store_set (list_store, &iter,
112                           0, gettext (s),
113                           -1);
114     }
115
116   return list_store;
117 }
118
119 /* Callback for when the alignment combo box
120    item is selected */
121 static void
122 change_alignment (GtkComboBox *cb,
123     gpointer user_data)
124 {
125   struct variable *pv = user_data;
126   gint active_item = gtk_combo_box_get_active (cb);
127
128   if ( active_item < 0 ) return ;
129
130   var_set_alignment (pv, active_item);
131 }
132
133
134
135 /* Callback for when the measure combo box
136    item is selected */
137 static void
138 change_measure (GtkComboBox *cb,
139     gpointer user_data)
140 {
141   struct variable *pv = user_data;
142   gint active_item = gtk_combo_box_get_active (cb);
143
144   if ( active_item < 0 ) return ;
145
146   var_set_measure (pv, active_item);
147 }
148
149
150
151 static gboolean
152 traverse_cell_callback (GtkSheet * sheet,
153                         gint row, gint column,
154                         gint *new_row, gint *new_column
155                         )
156 {
157   PsppireVarStore *var_store = PSPPIRE_VAR_STORE (gtk_sheet_get_model (sheet));
158
159   gint n_vars = psppire_var_store_get_var_cnt (var_store);
160
161   if ( row == n_vars && *new_row >= n_vars)
162     {
163       GtkEntry *entry = GTK_ENTRY (gtk_sheet_get_entry (sheet));
164
165       const gchar *name = gtk_entry_get_text (entry);
166
167       if (! psppire_dict_check_name (var_store->dict, name, TRUE))
168         return FALSE;
169
170       psppire_dict_insert_variable (var_store->dict, row, name);
171
172       return TRUE;
173     }
174
175   /* If the destination cell is outside the current  variables, then
176      automatically create variables for the new rows.
177   */
178   if ( (*new_row > n_vars) ||
179        (*new_row == n_vars && *new_column != COL_NAME) )
180     {
181       gint i;
182       for ( i = n_vars ; i <= *new_row; ++i )
183         psppire_dict_insert_variable (var_store->dict, i, NULL);
184     }
185
186   return TRUE;
187 }
188
189
190
191
192 /*
193    Callback whenever the cell on the var sheet is left
194 */
195 static gboolean
196 var_sheet_cell_entry_leave (GtkSheet * sheet, gint row, gint column,
197                             gpointer data)
198 {
199   gtk_sheet_change_entry (sheet, GTK_TYPE_ENTRY);
200   return TRUE;
201 }
202
203
204
205 /*
206    Callback whenever the cell on the var sheet is entered.
207 */
208 static gboolean
209 var_sheet_cell_entry_enter (GtkSheet * sheet, gint row, gint column,
210                             gpointer data)
211 {
212   GtkSheetCellAttr attributes;
213   PsppireVarStore *var_store ;
214   struct variable *pv ;
215
216   GladeXML *xml;
217
218   g_return_val_if_fail (sheet != NULL, FALSE);
219
220   var_store = PSPPIRE_VAR_STORE (gtk_sheet_get_model (sheet));
221
222   g_assert (var_store);
223
224   if ( row >= psppire_var_store_get_var_cnt (var_store))
225     return TRUE;
226
227   xml = glade_xml_new (PKGDATADIR "/data-editor.glade", NULL, NULL);
228
229   gtk_sheet_get_attributes (sheet, row, column, &attributes);
230
231   pv = psppire_var_store_get_var (var_store, row);
232
233   switch (column)
234     {
235     case COL_ALIGN:
236       {
237         static GtkListStore *list_store = 0;
238         GtkComboBoxEntry *cbe;
239         gtk_sheet_change_entry (sheet, GTK_TYPE_COMBO_BOX_ENTRY);
240         cbe =
241           GTK_COMBO_BOX_ENTRY (gtk_sheet_get_entry (sheet)->parent);
242
243
244         if ( ! list_store) list_store = create_label_list (alignments);
245
246         gtk_combo_box_set_model (GTK_COMBO_BOX (cbe),
247                                 GTK_TREE_MODEL (list_store));
248
249         gtk_combo_box_entry_set_text_column (cbe, 0);
250
251
252         g_signal_connect (G_OBJECT (cbe),"changed",
253                          G_CALLBACK (change_alignment), pv);
254       }
255       break;
256     case COL_MEASURE:
257       {
258         static GtkListStore *list_store = 0;
259         GtkComboBoxEntry *cbe;
260         gtk_sheet_change_entry (sheet, GTK_TYPE_COMBO_BOX_ENTRY);
261         cbe =
262           GTK_COMBO_BOX_ENTRY (gtk_sheet_get_entry (sheet)->parent);
263
264
265         if ( ! list_store) list_store = create_label_list (measures);
266
267         gtk_combo_box_set_model (GTK_COMBO_BOX (cbe),
268                                 GTK_TREE_MODEL (list_store));
269
270         gtk_combo_box_entry_set_text_column (cbe, 0);
271
272         g_signal_connect (G_OBJECT (cbe),"changed",
273                           G_CALLBACK (change_measure), pv);
274       }
275       break;
276
277     case COL_VALUES:
278       {
279         static struct val_labs_dialog *val_labs_dialog = 0;
280
281         PsppireCustomEntry *customEntry;
282
283         gtk_sheet_change_entry (sheet, PSPPIRE_CUSTOM_ENTRY_TYPE);
284
285         customEntry =
286           PSPPIRE_CUSTOM_ENTRY (gtk_sheet_get_entry (sheet));
287
288
289         if (!val_labs_dialog )
290             val_labs_dialog = val_labs_dialog_create (xml);
291
292         val_labs_dialog_set_target_variable (val_labs_dialog, pv);
293
294         g_signal_connect_swapped (GTK_OBJECT (customEntry),
295                                  "clicked",
296                                  GTK_SIGNAL_FUNC (val_labs_dialog_show),
297                                  val_labs_dialog);
298       }
299       break;
300     case COL_MISSING:
301       {
302         static struct missing_val_dialog *missing_val_dialog = 0;
303         PsppireCustomEntry *customEntry;
304
305         gtk_sheet_change_entry (sheet, PSPPIRE_CUSTOM_ENTRY_TYPE);
306
307         customEntry =
308           PSPPIRE_CUSTOM_ENTRY (gtk_sheet_get_entry (sheet));
309
310         if (!missing_val_dialog )
311             missing_val_dialog = missing_val_dialog_create (xml);
312
313         missing_val_dialog->pv = psppire_var_store_get_var (var_store, row);
314
315         g_signal_connect_swapped (GTK_OBJECT (customEntry),
316                                  "clicked",
317                                  GTK_SIGNAL_FUNC (missing_val_dialog_show),
318                                  missing_val_dialog);
319       }
320       break;
321
322     case COL_TYPE:
323       {
324         static struct var_type_dialog *var_type_dialog = 0;
325
326         PsppireCustomEntry *customEntry;
327
328         gtk_sheet_change_entry (sheet, PSPPIRE_CUSTOM_ENTRY_TYPE);
329
330         customEntry =
331           PSPPIRE_CUSTOM_ENTRY (gtk_sheet_get_entry (sheet));
332
333
334         /* Popup the Variable Type dialog box */
335         if (!var_type_dialog )
336             var_type_dialog = var_type_dialog_create (xml);
337
338
339         var_type_dialog->pv = pv;
340
341         g_signal_connect_swapped (GTK_OBJECT (customEntry),
342                                  "clicked",
343                                  GTK_SIGNAL_FUNC (var_type_dialog_show),
344                                  var_type_dialog);
345       }
346       break;
347     case COL_WIDTH:
348     case COL_DECIMALS:
349     case COL_COLUMNS:
350       {
351         if ( attributes.is_editable)
352           {
353             gint r_min, r_max;
354
355             const gchar *s = gtk_sheet_cell_get_text (sheet, row, column);
356
357             if (s)
358               {
359                 GtkSpinButton *spinButton ;
360                 const gint current_value  = atoi (s);
361                 GtkObject *adj ;
362
363                 const struct fmt_spec *fmt = var_get_write_format (pv);
364                 switch (column)
365                   {
366                   case COL_WIDTH:
367                     r_min = MAX (fmt->d + 1, fmt_min_output_width (fmt->type));
368                     r_max = fmt_max_output_width (fmt->type);
369                     break;
370                   case COL_DECIMALS:
371                     r_min = 0 ;
372                     r_max = fmt_max_output_decimals (fmt->type, fmt->w);
373                     break;
374                   case COL_COLUMNS:
375                     r_min = 1;
376                     r_max = 255 ; /* Is this a sensible value ? */
377                     break;
378                   default:
379                     g_assert_not_reached ();
380                   }
381
382                 adj = gtk_adjustment_new (current_value,
383                                          r_min, r_max,
384                                          1.0, 1.0, 1.0 /* steps */
385                                          );
386
387                 gtk_sheet_change_entry (sheet, GTK_TYPE_SPIN_BUTTON);
388
389                 spinButton =
390                   GTK_SPIN_BUTTON (gtk_sheet_get_entry (sheet));
391
392                 gtk_spin_button_set_adjustment (spinButton, GTK_ADJUSTMENT (adj));
393                 gtk_spin_button_set_digits (spinButton, 0);
394               }
395           }
396       }
397       break;
398
399     default:
400       gtk_sheet_change_entry (sheet, GTK_TYPE_ENTRY);
401       break;
402     }
403
404
405   g_object_unref (xml);
406
407   return TRUE;
408 }
409
410
411 extern PsppireVarStore *the_var_store;
412
413
414 /* Create the var sheet */
415 G_MODULE_EXPORT GtkWidget*
416 psppire_variable_sheet_create (gchar *widget_name,
417                                gchar *string1,
418                                gchar *string2,
419                                gint int1, gint int2)
420 {
421   gchar *codeset;
422   gint i;
423   GtkWidget *sheet;
424
425   GObject *geo = g_sheet_hetero_column_new (75, n_COLS);
426
427   g_assert (the_var_store);
428
429   sheet = gtk_sheet_new (G_SHEET_ROW (the_var_store),
430                         G_SHEET_COLUMN (geo),
431                         "variable sheet", 0);
432
433
434   g_signal_connect (GTK_OBJECT (sheet), "activate",
435                     GTK_SIGNAL_FUNC (var_sheet_cell_entry_enter),
436                     0);
437
438   g_signal_connect (GTK_OBJECT (sheet), "deactivate",
439                     GTK_SIGNAL_FUNC (var_sheet_cell_entry_leave),
440                     0);
441
442   g_signal_connect (GTK_OBJECT (sheet), "traverse",
443                     GTK_SIGNAL_FUNC (traverse_cell_callback), 0);
444
445
446   gtk_sheet_set_model (GTK_SHEET (sheet), G_SHEET_MODEL (the_var_store));
447
448
449 #if HAVE_LANGINFO_H
450   /* Since this happens inside glade_xml_new, we must prevent strings from
451    * being re-encoded twice */
452   codeset = bind_textdomain_codeset (PACKAGE, 0);
453   bind_textdomain_codeset (PACKAGE, nl_langinfo (CODESET));
454 #endif
455   for (i = 0 ; i < n_COLS ; ++i )
456     {
457       g_sheet_hetero_column_set_button_label (G_SHEET_HETERO_COLUMN (geo), i,
458                         gettext (column_def[i].label));
459
460       g_sheet_hetero_column_set_width (G_SHEET_HETERO_COLUMN (geo), i,
461                                                column_def[i].width);
462     }
463 #if HAVE_LANGINFO_H
464   bind_textdomain_codeset (PACKAGE, codeset);
465 #endif
466
467   gtk_widget_show (sheet);
468
469   return sheet;
470 }
471
472