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