Fixed a lot of internationalisation issues.
[pspp-builds.git] / src / ui / gui / psppire-var-store.c
1 /* psppire-var-store.c
2  
3    PSPPIRE --- A Graphical User Interface for PSPP
4    Copyright (C) 2006  Free Software Foundation
5    Written by John Darrington
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20    02110-1301, USA. */
21
22 #include <config.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <gettext.h>
26 #define _(msgid) gettext (msgid)
27 #define N_(msgid) msgid
28
29
30
31 #include <gobject/gvaluecollector.h>
32
33 #include <gtksheet/gsheetmodel.h>
34
35 #include "psppire-variable.h"
36 #include "psppire-var-store.h"
37 #include "var-sheet.h"
38 #include "helper.h"
39
40 #include <data/dictionary.h>
41 #include <data/variable.h>
42 #include <data/missing-values.h>
43
44 #include "val-labs-dialog.h"
45 #include "missing-val-dialog.h"
46 #include <data/value-labels.h>
47
48
49
50 static void         psppire_var_store_init            (PsppireVarStore      *var_store);
51 static void         psppire_var_store_class_init      (PsppireVarStoreClass *class);
52 static void         psppire_var_store_sheet_model_init (GSheetModelIface *iface);
53 static void         psppire_var_store_finalize        (GObject           *object);
54
55 static gchar *psppire_var_store_get_string(GSheetModel *sheet_model, gint row, gint column);
56
57 static gboolean  psppire_var_store_clear(GSheetModel *model,  gint row, gint col);
58
59
60 static gboolean psppire_var_store_set_string(GSheetModel *model, 
61                                           const gchar *text, gint row, gint column);
62
63
64 static gchar *text_for_column(const struct PsppireVariable *pv, gint c, GError **err);
65
66
67 static GObjectClass *parent_class = NULL;
68
69 GType
70 psppire_var_store_get_type (void)
71 {
72   static GType var_store_type = 0;
73
74   if (!var_store_type)
75     {
76       static const GTypeInfo var_store_info =
77       {
78         sizeof (PsppireVarStoreClass),
79         NULL,           /* base_init */
80         NULL,           /* base_finalize */
81         (GClassInitFunc) psppire_var_store_class_init,
82         NULL,           /* class_finalize */
83         NULL,           /* class_data */
84         sizeof (PsppireVarStore),
85         0,
86         (GInstanceInitFunc) psppire_var_store_init,
87       };
88
89       static const GInterfaceInfo sheet_model_info =
90       {
91         (GInterfaceInitFunc) psppire_var_store_sheet_model_init,
92         NULL,
93         NULL
94       };
95
96       var_store_type = g_type_register_static (G_TYPE_OBJECT, "PsppireVarStore",
97                                                 &var_store_info, 0);
98
99       g_type_add_interface_static (var_store_type,
100                                    G_TYPE_SHEET_MODEL,
101                                    &sheet_model_info);
102     }
103
104   return var_store_type;
105 }
106
107 static void
108 psppire_var_store_class_init (PsppireVarStoreClass *class)
109 {
110   GObjectClass *object_class;
111
112   parent_class = g_type_class_peek_parent (class);
113   object_class = (GObjectClass*) class;
114
115   object_class->finalize = psppire_var_store_finalize;
116 }
117
118
119 static void
120 psppire_var_store_init (PsppireVarStore *var_store)
121 {
122   GdkColormap *colormap = gdk_colormap_get_system();
123
124   g_assert(gdk_color_parse("gray", &var_store->disabled));
125
126   gdk_colormap_alloc_color (colormap, &var_store->disabled, FALSE, TRUE);
127
128   var_store->dict = 0;
129 }
130
131 static gboolean
132 psppire_var_store_item_editable(PsppireVarStore *var_store, gint row, gint column)
133 {
134   const struct fmt_spec *write_spec ;
135
136   struct PsppireVariable *pv = psppire_var_store_get_variable(var_store, row);
137
138   if ( !pv ) 
139     return TRUE;
140
141   if ( ALPHA == psppire_variable_get_type(pv) && column == COL_DECIMALS ) 
142     return FALSE;
143
144   write_spec = psppire_variable_get_write_spec(pv);
145
146   switch ( write_spec->type ) 
147     {
148     case FMT_DATE:      
149     case FMT_EDATE:     
150     case FMT_SDATE:     
151     case FMT_ADATE:     
152     case FMT_JDATE:     
153     case FMT_QYR:       
154     case FMT_MOYR:      
155     case FMT_WKYR:      
156     case FMT_DATETIME:  
157     case FMT_TIME:      
158     case FMT_DTIME:     
159     case FMT_WKDAY:     
160     case FMT_MONTH:     
161       if ( column == COL_DECIMALS || column == COL_WIDTH)
162         return FALSE;
163       break;
164     default:
165       break;
166     }
167
168   return TRUE;
169 }
170
171 static gboolean
172 psppire_var_store_is_editable(GSheetModel *model, gint row, gint column)
173 {
174   PsppireVarStore *store = PSPPIRE_VAR_STORE(model);
175   return psppire_var_store_item_editable(store, row, column);
176 }
177
178
179 static const GdkColor *
180 psppire_var_store_get_foreground(GSheetModel *model, gint row, gint column)
181 {
182   PsppireVarStore *store = PSPPIRE_VAR_STORE(model);
183
184   if ( ! psppire_var_store_item_editable(store, row, column) ) 
185     return &store->disabled;
186   
187   return NULL;
188 }
189
190
191 const PangoFontDescription *
192 psppire_var_store_get_font_desc(GSheetModel *model,
193                               gint row, gint column)
194 {
195   PsppireVarStore *store = PSPPIRE_VAR_STORE(model);
196   
197   return store->font_desc;
198 }
199
200
201
202 static void
203 psppire_var_store_sheet_model_init (GSheetModelIface *iface)
204 {
205   iface->free_strings = TRUE;
206   iface->get_string = psppire_var_store_get_string;
207   iface->set_string = psppire_var_store_set_string;
208   iface->clear_datum = psppire_var_store_clear;
209   iface->is_editable = psppire_var_store_is_editable;
210   iface->is_visible = NULL;
211   iface->get_foreground = psppire_var_store_get_foreground;
212   iface->get_background = NULL;
213   iface->get_font_desc = psppire_var_store_get_font_desc;
214   iface->get_cell_border = NULL;
215 }
216
217
218
219 /**
220  * psppire_var_store_new:
221  * @dict: The dictionary for this var_store.
222  *
223  *
224  * Return value: a new #PsppireVarStore
225  **/
226 PsppireVarStore *
227 psppire_var_store_new (PsppireDict *dict)
228 {
229   PsppireVarStore *retval;
230
231   retval = g_object_new (GTK_TYPE_VAR_STORE, NULL);
232
233   psppire_var_store_set_dictionary(retval, dict);
234
235   return retval;
236 }
237
238 static void 
239 var_change_callback(GtkWidget *w, gint n, gpointer data)
240 {
241   GSheetModel *model = G_SHEET_MODEL(data);
242   g_sheet_model_range_changed (model,
243                                  n, 0, n, n_COLS);
244 }
245
246
247 static void 
248 var_delete_callback(GtkWidget *w, gint first, gint n, gpointer data)
249 {
250   GSheetModel *model = G_SHEET_MODEL(data);
251   
252   g_sheet_model_rows_deleted (model, first, n);
253 }
254
255
256
257 static void 
258 var_insert_callback(GtkWidget *w, gint row, gpointer data)
259 {
260   GSheetModel *model = G_SHEET_MODEL(data);
261
262   g_sheet_model_rows_inserted (model, row, 1);
263 }
264
265
266
267 /**
268  * psppire_var_store_replace_set_dictionary:
269  * @var_store: The variable store
270  * @dict: The dictionary to set
271  *
272  * If a dictionary is already associated with the var-store, then it will be
273  * destroyed.
274  **/
275 void
276 psppire_var_store_set_dictionary(PsppireVarStore *var_store, PsppireDict *dict)
277 {
278   if ( var_store->dict ) g_object_unref(var_store->dict);
279
280   var_store->dict = dict;
281
282   g_signal_connect(dict, "variable-changed", G_CALLBACK(var_change_callback), 
283                    var_store);
284
285   g_signal_connect(dict, "variables-deleted", G_CALLBACK(var_delete_callback), 
286                    var_store);
287
288   g_signal_connect(dict, "variable-inserted", G_CALLBACK(var_insert_callback), 
289                    var_store);
290
291
292   /* The entire model has changed */
293   g_sheet_model_range_changed (G_SHEET_MODEL(var_store), -1, -1, -1, -1);
294 }
295
296 static void
297 psppire_var_store_finalize (GObject *object)
298 {
299   /* must chain up */
300   (* parent_class->finalize) (object);
301 }
302
303 static gchar *
304 psppire_var_store_get_string(GSheetModel *model, gint row, gint column)
305 {
306   PsppireVarStore *store = PSPPIRE_VAR_STORE(model);
307
308   struct PsppireVariable *pv;
309
310   if ( row >= psppire_dict_get_var_cnt(store->dict))
311     return 0;
312   
313   pv = psppire_dict_get_variable (store->dict, row);
314   
315   return text_for_column(pv, column, 0);
316 }
317
318
319 struct PsppireVariable *
320 psppire_var_store_get_variable(PsppireVarStore *store, gint row)
321 {
322   g_return_val_if_fail(store, NULL);
323   g_return_val_if_fail(store->dict, NULL);
324
325   if ( row >= psppire_dict_get_var_cnt(store->dict))
326     return 0;
327
328   return psppire_dict_get_variable (store->dict, row);
329 }
330
331 /* Clears that part of the variable store, if possible, which corresponds 
332    to ROW, COL.
333    Returns true if anything was updated, false otherwise.
334 */
335 static gboolean 
336 psppire_var_store_clear(GSheetModel *model,  gint row, gint col)
337 {
338   struct PsppireVariable *pv ;
339
340   PsppireVarStore *var_store = PSPPIRE_VAR_STORE(model);
341
342   if ( row >= psppire_dict_get_var_cnt(var_store->dict))
343       return FALSE;
344
345   pv = psppire_var_store_get_variable(var_store, row);
346
347   if ( !pv ) 
348     return FALSE;
349
350   switch (col)
351     {
352     case COL_LABEL:
353       psppire_variable_set_label(pv, 0);
354       return TRUE;
355       break;
356     }
357
358   return FALSE;
359 }
360
361 /* Attempts to update that part of the variable store which corresponds 
362    to ROW, COL with  the value TEXT.
363    Returns true if anything was updated, false otherwise.
364 */
365 static gboolean 
366 psppire_var_store_set_string(GSheetModel *model, 
367                           const gchar *text, gint row, gint col)
368 {
369   struct PsppireVariable *pv ;
370
371   PsppireVarStore *var_store = PSPPIRE_VAR_STORE(model);
372
373   if ( row >= psppire_dict_get_var_cnt(var_store->dict))
374       return FALSE;
375
376   pv = psppire_var_store_get_variable(var_store, row);
377   if ( !pv ) 
378     return FALSE;
379
380   switch (col)
381     {
382     case COL_NAME:
383       return psppire_variable_set_name(pv, text);
384       break;
385     case COL_COLUMNS:
386       if ( ! text) return FALSE;
387       return psppire_variable_set_columns(pv, atoi(text));
388       break;
389     case COL_WIDTH:
390       if ( ! text) return FALSE;
391       return psppire_variable_set_width(pv, atoi(text));
392       break;
393     case COL_DECIMALS:
394       if ( ! text) return FALSE;
395       return psppire_variable_set_decimals(pv, atoi(text));
396       break;
397     case COL_LABEL:
398       psppire_variable_set_label(pv, text);
399       return TRUE;
400       break;
401     case COL_TYPE:
402     case COL_VALUES:
403     case COL_MISSING:
404     case COL_ALIGN:
405     case COL_MEASURE:
406       /* These can be modified only by their respective dialog boxes */
407       return FALSE;
408       break;
409     default:
410       g_assert_not_reached();
411       return FALSE;
412     }
413
414   return TRUE;
415 }
416
417
418 static  gchar *
419 text_for_column(const struct PsppireVariable *pv, gint c, GError **err)
420 {
421   static gchar none[] = N_("None");
422
423   static const gchar *const type_label[] = 
424     {
425       N_("Numeric"),
426       N_("Comma"),
427       N_("Dot"),
428       N_("Scientific"),
429       N_("Date"),
430       N_("Dollar"),
431       N_("Custom"),
432       N_("String")
433     };
434   enum {VT_NUMERIC, VT_COMMA, VT_DOT, VT_SCIENTIFIC, VT_DATE, VT_DOLLAR, 
435         VT_CUSTOM, VT_STRING};
436
437   const struct fmt_spec *write_spec = psppire_variable_get_write_spec(pv);
438
439   switch (c)
440     {
441     case COL_NAME:
442       return pspp_locale_to_utf8(psppire_variable_get_name(pv), -1, err);
443       break;
444     case COL_TYPE:
445       {
446         switch ( write_spec->type ) 
447           {
448           case FMT_F:
449             return g_locale_to_utf8(gettext(type_label[VT_NUMERIC]), -1, 0, 0, err);
450             break;
451           case FMT_COMMA:
452             return g_locale_to_utf8(gettext(type_label[VT_COMMA]), -1, 0, 0, err);
453             break;
454           case FMT_DOT:
455             return g_locale_to_utf8(gettext(type_label[VT_DOT]), -1, 0, 0, err);
456             break;
457           case FMT_E:
458             return g_locale_to_utf8(gettext(type_label[VT_SCIENTIFIC]), -1, 0, 0, err);
459             break;
460           case FMT_DATE:        
461           case FMT_EDATE:       
462           case FMT_SDATE:       
463           case FMT_ADATE:       
464           case FMT_JDATE:       
465           case FMT_QYR: 
466           case FMT_MOYR:        
467           case FMT_WKYR:        
468           case FMT_DATETIME:    
469           case FMT_TIME:        
470           case FMT_DTIME:       
471           case FMT_WKDAY:       
472           case FMT_MONTH:       
473             return g_locale_to_utf8(type_label[VT_DATE], -1, 0, 0, err);
474             break;
475           case FMT_DOLLAR:
476             return g_locale_to_utf8(type_label[VT_DOLLAR], -1, 0, 0, err);
477             break;
478           case FMT_CCA:
479           case FMT_CCB:
480           case FMT_CCC:
481           case FMT_CCD:
482           case FMT_CCE:
483             return g_locale_to_utf8(gettext(type_label[VT_CUSTOM]), -1, 0, 0, err);
484             break;
485           case FMT_A:
486             return g_locale_to_utf8(gettext(type_label[VT_STRING]), -1, 0, 0, err);
487             break;
488           default:
489             g_warning("Unknown format: \"%s\"\n", 
490                       fmt_to_string(write_spec));
491             break;
492           }
493       }
494       break;
495     case COL_WIDTH:
496       {
497         gchar *s;
498         GString *gstr = g_string_sized_new(10);
499         g_string_printf(gstr, "%d", write_spec->w);
500         s = g_locale_to_utf8(gstr->str, gstr->len, 0, 0, err);
501         g_string_free(gstr, TRUE);
502         return s;
503       }
504       break;
505     case COL_DECIMALS:
506       {
507         gchar *s;
508         GString *gstr = g_string_sized_new(10);
509         g_string_printf(gstr, "%d", write_spec->d);
510         s = g_locale_to_utf8(gstr->str, gstr->len, 0, 0, err);
511         g_string_free(gstr, TRUE);
512         return s;
513       }
514       break;
515     case COL_COLUMNS:
516       {
517         gchar *s;
518         GString *gstr = g_string_sized_new(10);
519         g_string_printf(gstr, "%d", psppire_variable_get_columns(pv));
520         s = g_locale_to_utf8(gstr->str, gstr->len, 0, 0, err);
521         g_string_free(gstr, TRUE);
522         return s;
523       }
524       break;
525     case COL_LABEL:
526       return pspp_locale_to_utf8(psppire_variable_get_label(pv), -1, err);
527       break;
528
529     case COL_MISSING:
530       {
531         gchar *s;
532         const struct missing_values *miss = psppire_variable_get_missing(pv);
533         if ( mv_is_empty(miss)) 
534           return g_locale_to_utf8(gettext(none), -1, 0, 0, err);
535         else
536           {
537             if ( ! mv_has_range (miss))
538               {
539                 GString *gstr = g_string_sized_new(10);
540                 const int n = mv_n_values(miss);
541                 gchar *mv[4] = {0,0,0,0};
542                 gint i;
543                 for(i = 0 ; i < n; ++i ) 
544                   {
545                     union value v;
546                     mv_peek_value(miss, &v, i);
547                     mv[i] = value_to_text(v, *write_spec);
548                     if ( i > 0 ) 
549                       g_string_append(gstr, ", ");
550                     g_string_append(gstr, mv[i]);
551                     g_free(mv[i]);
552                   }
553                 s = pspp_locale_to_utf8(gstr->str, gstr->len, err);
554                 g_string_free(gstr, TRUE);
555               }
556             else
557               {
558                 GString *gstr = g_string_sized_new(10);
559                 gchar *l, *h;
560                 union value low, high;
561                 mv_peek_range(miss, &low.f, &high.f);
562                   
563                 l = value_to_text(low, *write_spec);
564                 h = value_to_text(high, *write_spec);
565
566                 g_string_printf(gstr, "%s - %s", l, h);
567                 g_free(l);
568                 g_free(h);
569
570                 if ( mv_has_value(miss)) 
571                   {
572                     gchar *ss = 0;
573                     union value v;
574                     mv_peek_value(miss, &v, 0);
575
576                     ss = value_to_text(v, *write_spec);
577
578                     g_string_append(gstr, ", ");
579                     g_string_append(gstr, ss);
580                     free(ss);
581                   }
582                 s = pspp_locale_to_utf8(gstr->str, gstr->len, err);
583                 g_string_free(gstr, TRUE);
584               }
585
586             return s;
587           }
588       }
589       break;
590     case COL_VALUES:
591       {
592         const struct val_labs *vls = psppire_variable_get_value_labels(pv);
593         if ( ! vls || 0 == val_labs_count(vls) ) 
594           return g_locale_to_utf8(gettext(none), -1, 0, 0, err);
595         else
596           {
597             gchar *ss;
598             GString *gstr = g_string_sized_new(10);
599             struct val_labs_iterator *ip = 0;
600             struct val_lab *vl = val_labs_first_sorted (vls, &ip);
601
602             g_assert(vl);
603
604             {
605               gchar *const vstr = value_to_text(vl->value, *write_spec);
606
607               g_string_printf(gstr, "{%s,\"%s\"}_", vstr, vl->label);
608               g_free(vstr);
609             }
610
611             val_labs_done(&ip);
612             
613             ss = pspp_locale_to_utf8(gstr->str, gstr->len, err);
614             g_string_free(gstr, TRUE);
615             return ss;
616           }
617       }
618       break;
619     case COL_ALIGN:
620       return g_locale_to_utf8(gettext(alignments[psppire_variable_get_alignment(pv)]), 
621                                          -1, -0, 0, err);
622       break;
623     case COL_MEASURE:
624       return g_locale_to_utf8(gettext(measures[psppire_variable_get_measure(pv)]), 
625                                          -1, -0, 0, err);
626       break;
627
628     }
629   return 0;
630 }
631
632
633
634 /* Return the number of variables */
635 gint
636 psppire_var_store_get_var_cnt(PsppireVarStore  *store)
637 {
638   return psppire_dict_get_var_cnt(store->dict);
639 }
640
641
642 void
643 psppire_var_store_set_font(PsppireVarStore *store, PangoFontDescription *fd)
644 {
645   g_return_if_fail (store);
646   g_return_if_fail (PSPPIRE_IS_VAR_STORE (store));
647
648   store->font_desc = fd;
649
650   g_sheet_model_range_changed (G_SHEET_MODEL(store), -1, -1, -1, -1);
651 }
652
653
654
655