Fixed some memory leaks in GUI.
[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 const 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 const gchar *text_for_column(const struct PsppireVariable *pv, gint c);
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 const gchar *
301 psppire_var_store_get_string(GSheetModel *model, gint row, gint column)
302 {
303   const gchar *s ;
304
305   PsppireVarStore *store = PSPPIRE_VAR_STORE(model);
306
307   struct PsppireVariable *pv;
308
309   if ( row >= psppire_dict_get_var_cnt(store->dict))
310     return 0;
311   
312   pv = psppire_dict_get_variable (store->dict, row);
313   
314   s = text_for_column(pv, column);
315
316   return pspp_locale_to_utf8(s, -1, 0);
317 }
318
319
320 struct PsppireVariable *
321 psppire_var_store_get_variable(PsppireVarStore *store, gint row)
322 {
323   g_return_val_if_fail(store, NULL);
324   g_return_val_if_fail(store->dict, NULL);
325
326   if ( row >= psppire_dict_get_var_cnt(store->dict))
327     return 0;
328
329   return psppire_dict_get_variable (store->dict, row);
330 }
331
332 /* Clears that part of the variable store, if possible, which corresponds 
333    to ROW, COL.
334    Returns true if anything was updated, false otherwise.
335 */
336 static gboolean 
337 psppire_var_store_clear(GSheetModel *model,  gint row, gint col)
338 {
339   struct PsppireVariable *pv ;
340
341   PsppireVarStore *var_store = PSPPIRE_VAR_STORE(model);
342
343   if ( row >= psppire_dict_get_var_cnt(var_store->dict))
344       return FALSE;
345
346   pv = psppire_var_store_get_variable(var_store, row);
347
348   if ( !pv ) 
349     return FALSE;
350
351   switch (col)
352     {
353     case COL_LABEL:
354       psppire_variable_set_label(pv, 0);
355       return TRUE;
356       break;
357     }
358
359   return FALSE;
360 }
361
362 /* Attempts to update that part of the variable store which corresponds 
363    to ROW, COL with  the value TEXT.
364    Returns true if anything was updated, false otherwise.
365 */
366 static gboolean 
367 psppire_var_store_set_string(GSheetModel *model, 
368                           const gchar *text, gint row, gint col)
369 {
370   struct PsppireVariable *pv ;
371
372   PsppireVarStore *var_store = PSPPIRE_VAR_STORE(model);
373
374   if ( row >= psppire_dict_get_var_cnt(var_store->dict))
375       return FALSE;
376
377   pv = psppire_var_store_get_variable(var_store, row);
378   if ( !pv ) 
379     return FALSE;
380
381   switch (col)
382     {
383     case COL_NAME:
384       return psppire_variable_set_name(pv, text);
385       break;
386     case COL_COLUMNS:
387       if ( ! text) return FALSE;
388       return psppire_variable_set_columns(pv, atoi(text));
389       break;
390     case COL_WIDTH:
391       if ( ! text) return FALSE;
392       return psppire_variable_set_width(pv, atoi(text));
393       break;
394     case COL_DECIMALS:
395       if ( ! text) return FALSE;
396       return psppire_variable_set_decimals(pv, atoi(text));
397       break;
398     case COL_LABEL:
399       psppire_variable_set_label(pv, text);
400       return TRUE;
401       break;
402     case COL_TYPE:
403     case COL_VALUES:
404     case COL_MISSING:
405     case COL_ALIGN:
406     case COL_MEASURE:
407       /* These can be modified only by their respective dialog boxes */
408       return FALSE;
409       break;
410     default:
411       g_assert_not_reached();
412       return FALSE;
413     }
414
415   return TRUE;
416 }
417
418
419 #define MAX_CELL_TEXT_LEN 255
420
421 static const gchar *
422 text_for_column(const struct PsppireVariable *pv, gint c)
423 {
424   static gchar buf[MAX_CELL_TEXT_LEN];
425
426   static gchar none[]=_("None");
427
428   static const gchar *const type_label[] = 
429     {
430       _("Numeric"),
431       _("Comma"),
432       _("Dot"),
433       _("Scientific"),
434       _("Date"),
435       _("Dollar"),
436       _("Custom"),
437       _("String")
438     };
439   enum {VT_NUMERIC, VT_COMMA, VT_DOT, VT_SCIENTIFIC, VT_DATE, VT_DOLLAR, 
440         VT_CUSTOM, VT_STRING};
441
442   const struct fmt_spec *write_spec = psppire_variable_get_write_spec(pv);
443
444   switch (c)
445     {
446     case COL_NAME:
447       return psppire_variable_get_name(pv);
448       break;
449     case COL_TYPE:
450       {
451         switch ( write_spec->type ) 
452           {
453           case FMT_F:
454             return type_label[VT_NUMERIC];
455             break;
456           case FMT_COMMA:
457             return type_label[VT_COMMA];
458             break;
459           case FMT_DOT:
460             return type_label[VT_DOT];
461             break;
462           case FMT_E:
463             return type_label[VT_SCIENTIFIC];
464             break;
465           case FMT_DATE:        
466           case FMT_EDATE:       
467           case FMT_SDATE:       
468           case FMT_ADATE:       
469           case FMT_JDATE:       
470           case FMT_QYR: 
471           case FMT_MOYR:        
472           case FMT_WKYR:        
473           case FMT_DATETIME:    
474           case FMT_TIME:        
475           case FMT_DTIME:       
476           case FMT_WKDAY:       
477           case FMT_MONTH:       
478             return type_label[VT_DATE];
479             break;
480           case FMT_DOLLAR:
481             return type_label[VT_DOLLAR];
482             break;
483           case FMT_CCA:
484           case FMT_CCB:
485           case FMT_CCC:
486           case FMT_CCD:
487           case FMT_CCE:
488             return type_label[VT_CUSTOM];
489             break;
490           case FMT_A:
491             return type_label[VT_STRING];
492             break;
493           default:
494             g_warning("Unknown format: \"%s\"\n", 
495                       fmt_to_string(write_spec));
496             break;
497           }
498       }
499       break;
500     case COL_WIDTH:
501       {
502         g_snprintf(buf, MAX_CELL_TEXT_LEN, "%d", write_spec->w);
503         return buf;
504       }
505       break;
506     case COL_DECIMALS:
507       {
508         g_snprintf(buf, MAX_CELL_TEXT_LEN, "%d", write_spec->d);
509         return buf;
510       }
511       break;
512     case COL_COLUMNS:
513       {
514         g_snprintf(buf, MAX_CELL_TEXT_LEN, 
515                    "%d", psppire_variable_get_columns(pv));
516         return buf;
517       }
518       break;
519     case COL_LABEL:
520       return psppire_variable_get_label(pv);
521       break;
522     case COL_MISSING:
523       {
524       const struct missing_values *miss = psppire_variable_get_missing(pv);
525       if ( mv_is_empty(miss)) 
526         return none;
527       else
528         {
529           if ( ! mv_has_range (miss))
530             {
531               const int n = mv_n_values(miss);
532               gchar *mv[4] = {0,0,0,0};
533               gint i;
534               for(i = 0 ; i < n; ++i ) 
535                 {
536                   union value v;
537                   mv_peek_value(miss, &v, i);
538                   mv[i] = value_to_text(v, *write_spec);
539                 }
540               g_stpcpy(buf, "");
541               for(i = 0 ; i < n; ++i ) 
542                 {
543                   if ( i > 0) 
544                     g_strlcat(buf, ", ", MAX_CELL_TEXT_LEN);
545                   g_strlcat(buf, mv[i], MAX_CELL_TEXT_LEN);
546                   g_free(mv[i]);
547                 }
548             }
549           else
550             {
551               gchar *l, *h;
552               union value low, high;
553               mv_peek_range(miss, &low.f, &high.f);
554                   
555               l = value_to_text(low, *write_spec);
556               h = value_to_text(high, *write_spec);
557
558               g_snprintf(buf, MAX_CELL_TEXT_LEN, "%s - %s", l, h);
559               g_free(l);
560               g_free(h);
561
562               if ( mv_has_value(miss)) 
563                 {
564                   gchar buf2[MAX_CELL_TEXT_LEN];
565                   gchar *s = 0;
566                   union value v;
567                   mv_peek_value(miss, &v, 0);
568
569                   s = value_to_text(v, *write_spec);
570
571                   g_snprintf(buf2, MAX_CELL_TEXT_LEN, "%s, %s", buf, s);
572                   free(s);
573                   g_stpcpy(buf, buf2);
574                 }
575             }
576
577           return buf;
578         }
579       }
580       break;
581     case COL_VALUES:
582       {
583         const struct val_labs *vls = psppire_variable_get_value_labels(pv);
584         if ( ! vls || 0 == val_labs_count(vls)) 
585           return none;
586         else
587           {
588             struct val_labs_iterator *ip=0;
589             struct val_lab *vl = val_labs_first_sorted (vls, &ip);
590
591             g_assert(vl);
592
593             {
594               gchar *const vstr = value_to_text(vl->value, *write_spec);
595
596               g_snprintf(buf, MAX_CELL_TEXT_LEN, "{%s,\"%s\"}_", vstr, vl->label);
597               g_free(vstr);
598             }
599
600             val_labs_done(&ip);
601
602             return buf;
603           }
604       }
605       break;
606     case COL_ALIGN:
607       return alignments[psppire_variable_get_alignment(pv)];
608       break;
609     case COL_MEASURE:
610       return measures[psppire_variable_get_measure(pv)];
611       break;
612     }
613   return 0;
614 }
615
616
617
618 /* Return the number of variables */
619 gint
620 psppire_var_store_get_var_cnt(PsppireVarStore  *store)
621 {
622   return psppire_dict_get_var_cnt(store->dict);
623 }
624
625
626 void
627 psppire_var_store_set_font(PsppireVarStore *store, PangoFontDescription *fd)
628 {
629   g_return_if_fail (store);
630   g_return_if_fail (PSPPIRE_IS_VAR_STORE (store));
631
632   store->font_desc = fd;
633
634   g_sheet_model_range_changed (G_SHEET_MODEL(store), -1, -1, -1, -1);
635 }
636
637
638