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