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