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