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