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