Fixed a number of warnings.
[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->get_string = psppire_var_store_get_string;
203   iface->set_string = psppire_var_store_set_string;
204   iface->clear_datum = psppire_var_store_clear;
205   iface->is_editable = psppire_var_store_is_editable;
206   iface->is_visible = NULL;
207   iface->get_foreground = psppire_var_store_get_foreground;
208   iface->get_background = NULL;
209   iface->get_font_desc = psppire_var_store_get_font_desc;
210   iface->get_cell_border = NULL;
211 }
212
213
214
215 /**
216  * psppire_var_store_new:
217  * @dict: The dictionary for this var_store.
218  *
219  *
220  * Return value: a new #PsppireVarStore
221  **/
222 PsppireVarStore *
223 psppire_var_store_new (PsppireDict *dict)
224 {
225   PsppireVarStore *retval;
226
227   retval = g_object_new (GTK_TYPE_VAR_STORE, NULL);
228
229   psppire_var_store_set_dictionary(retval, dict);
230
231   return retval;
232 }
233
234 static void 
235 var_change_callback(GtkWidget *w, gint n, gpointer data)
236 {
237   GSheetModel *model = G_SHEET_MODEL(data);
238   g_sheet_model_range_changed (model,
239                                  n, 0, n, n_COLS);
240 }
241
242
243 static void 
244 var_delete_callback(GtkWidget *w, gint first, gint n, gpointer data)
245 {
246   GSheetModel *model = G_SHEET_MODEL(data);
247   
248   g_sheet_model_rows_deleted (model, first, n);
249 }
250
251
252
253 static void 
254 var_insert_callback(GtkWidget *w, gint row, gpointer data)
255 {
256   GSheetModel *model = G_SHEET_MODEL(data);
257
258   g_sheet_model_rows_inserted (model, row, 1);
259 }
260
261
262
263 /**
264  * psppire_var_store_replace_set_dictionary:
265  * @var_store: The variable store
266  * @dict: The dictionary to set
267  *
268  * If a dictionary is already associated with the var-store, then it will be
269  * destroyed.
270  **/
271 void
272 psppire_var_store_set_dictionary(PsppireVarStore *var_store, PsppireDict *dict)
273 {
274   if ( var_store->dict ) g_object_unref(var_store->dict);
275
276   var_store->dict = dict;
277
278   g_signal_connect(dict, "variable-changed", G_CALLBACK(var_change_callback), 
279                    var_store);
280
281   g_signal_connect(dict, "variables-deleted", G_CALLBACK(var_delete_callback), 
282                    var_store);
283
284   g_signal_connect(dict, "variable-inserted", G_CALLBACK(var_insert_callback), 
285                    var_store);
286
287
288   /* The entire model has changed */
289   g_sheet_model_range_changed (G_SHEET_MODEL(var_store), -1, -1, -1, -1);
290 }
291
292 static void
293 psppire_var_store_finalize (GObject *object)
294 {
295   /* must chain up */
296   (* parent_class->finalize) (object);
297 }
298
299 static const gchar *
300 psppire_var_store_get_string(GSheetModel *model, gint row, gint column)
301 {
302   const gchar *s ;
303
304   PsppireVarStore *store = PSPPIRE_VAR_STORE(model);
305
306   struct PsppireVariable *pv;
307
308   if ( row >= psppire_dict_get_var_cnt(store->dict))
309     return 0;
310   
311   pv = psppire_dict_get_variable (store->dict, row);
312   
313   s = text_for_column(pv, column);
314
315   return g_locale_to_utf8(s, -1, 0,0,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 #define MAX_CELL_TEXT_LEN 255
419
420 static const gchar *
421 text_for_column(const struct PsppireVariable *pv, gint c)
422 {
423   static gchar buf[MAX_CELL_TEXT_LEN];
424
425   static gchar none[]=_("None");
426
427   static const gchar *const type_label[] = 
428     {
429       _("Numeric"),
430       _("Comma"),
431       _("Dot"),
432       _("Scientific"),
433       _("Date"),
434       _("Dollar"),
435       _("Custom"),
436       _("String")
437     };
438   enum {VT_NUMERIC, VT_COMMA, VT_DOT, VT_SCIENTIFIC, VT_DATE, VT_DOLLAR, 
439         VT_CUSTOM, VT_STRING};
440
441   const struct fmt_spec *write_spec = psppire_variable_get_write_spec(pv);
442
443   switch (c)
444     {
445     case COL_NAME:
446       return psppire_variable_get_name(pv);
447       break;
448     case COL_TYPE:
449       {
450         switch ( write_spec->type ) 
451           {
452           case FMT_F:
453             return type_label[VT_NUMERIC];
454             break;
455           case FMT_COMMA:
456             return type_label[VT_COMMA];
457             break;
458           case FMT_DOT:
459             return type_label[VT_DOT];
460             break;
461           case FMT_E:
462             return type_label[VT_SCIENTIFIC];
463             break;
464           case FMT_DATE:        
465           case FMT_EDATE:       
466           case FMT_SDATE:       
467           case FMT_ADATE:       
468           case FMT_JDATE:       
469           case FMT_QYR: 
470           case FMT_MOYR:        
471           case FMT_WKYR:        
472           case FMT_DATETIME:    
473           case FMT_TIME:        
474           case FMT_DTIME:       
475           case FMT_WKDAY:       
476           case FMT_MONTH:       
477             return type_label[VT_DATE];
478             break;
479           case FMT_DOLLAR:
480             return type_label[VT_DOLLAR];
481             break;
482           case FMT_CCA:
483           case FMT_CCB:
484           case FMT_CCC:
485           case FMT_CCD:
486           case FMT_CCE:
487             return type_label[VT_CUSTOM];
488             break;
489           case FMT_A:
490             return type_label[VT_STRING];
491             break;
492           default:
493             g_warning("Unknown format: \"%s\"\n", 
494                       fmt_to_string(write_spec));
495             break;
496           }
497       }
498       break;
499     case COL_WIDTH:
500       {
501         g_snprintf(buf, MAX_CELL_TEXT_LEN, "%d", write_spec->w);
502         return buf;
503       }
504       break;
505     case COL_DECIMALS:
506       {
507         g_snprintf(buf, MAX_CELL_TEXT_LEN, "%d", write_spec->d);
508         return buf;
509       }
510       break;
511     case COL_COLUMNS:
512       {
513         g_snprintf(buf, MAX_CELL_TEXT_LEN, 
514                    "%d", psppire_variable_get_columns(pv));
515         return buf;
516       }
517       break;
518     case COL_LABEL:
519       return psppire_variable_get_label(pv);
520       break;
521     case COL_MISSING:
522       {
523       const struct missing_values *miss = psppire_variable_get_missing(pv);
524       if ( mv_is_empty(miss)) 
525         return none;
526       else
527         {
528           if ( ! mv_has_range (miss))
529             {
530               const int n = mv_n_values(miss);
531               gchar *mv[4] = {0,0,0,0};
532               gint i;
533               for(i = 0 ; i < n; ++i ) 
534                 {
535                   union value v;
536                   mv_peek_value(miss, &v, i);
537                   mv[i] = value_to_text(v, *write_spec);
538                 }
539               g_stpcpy(buf, "");
540               for(i = 0 ; i < n; ++i ) 
541                 {
542                   if ( i > 0) 
543                     g_strlcat(buf, ", ", MAX_CELL_TEXT_LEN);
544                   g_strlcat(buf, mv[i], MAX_CELL_TEXT_LEN);
545                   g_free(mv[i]);
546                 }
547             }
548           else
549             {
550               gchar *l, *h;
551               union value low, high;
552               mv_peek_range(miss, &low.f, &high.f);
553                   
554               l = value_to_text(low, *write_spec);
555               h = value_to_text(high, *write_spec);
556
557               g_snprintf(buf, MAX_CELL_TEXT_LEN, "%s - %s", l, h);
558               g_free(l);
559               g_free(h);
560
561               if ( mv_has_value(miss)) 
562                 {
563                   gchar buf2[MAX_CELL_TEXT_LEN];
564                   gchar *s = 0;
565                   union value v;
566                   mv_peek_value(miss, &v, 0);
567
568                   s = value_to_text(v, *write_spec);
569
570                   g_snprintf(buf2, MAX_CELL_TEXT_LEN, "%s, %s", buf, s);
571                   free(s);
572                   g_stpcpy(buf, buf2);
573                 }
574             }
575
576           return buf;
577         }
578       }
579       break;
580     case COL_VALUES:
581       {
582         const struct val_labs *vls = psppire_variable_get_value_labels(pv);
583         if ( ! vls || 0 == val_labs_count(vls)) 
584           return none;
585         else
586           {
587             struct val_labs_iterator *ip=0;
588             struct val_lab *vl = val_labs_first_sorted (vls, &ip);
589
590             g_assert(vl);
591
592             {
593               gchar *const vstr = value_to_text(vl->value, *write_spec);
594
595               g_snprintf(buf, MAX_CELL_TEXT_LEN, "{%s,\"%s\"}_", vstr, vl->label);
596               g_free(vstr);
597             }
598
599             val_labs_done(&ip);
600
601             return buf;
602           }
603       }
604       break;
605     case COL_ALIGN:
606       return alignments[psppire_variable_get_alignment(pv)];
607       break;
608     case COL_MEASURE:
609       return measures[psppire_variable_get_measure(pv)];
610       break;
611     }
612   return 0;
613 }
614
615
616
617 /* Return the number of variables */
618 gint
619 psppire_var_store_get_var_cnt(PsppireVarStore  *store)
620 {
621   return psppire_dict_get_var_cnt(store->dict);
622 }
623
624
625 void
626 psppire_var_store_set_font(PsppireVarStore *store, PangoFontDescription *fd)
627 {
628   g_return_if_fail (store);
629   g_return_if_fail (PSPPIRE_IS_VAR_STORE (store));
630
631   store->font_desc = fd;
632
633   g_sheet_model_range_changed (G_SHEET_MODEL(store), -1, -1, -1, -1);
634 }
635
636
637