Fixed some bad interaction between variable and data sheets.
[pspp-builds.git] / src / ui / gui / psppire-dict.c
1 /* 
2     PSPPIRE --- A Graphical User Interface for PSPP
3     Copyright (C) 2004, 2006  Free Software Foundation
4     Written by John Darrington
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19     02110-1301, USA. */
20
21
22 #include <string.h>
23 #include <stdlib.h>
24
25 #include <gtksheet/gtkextra-marshal.c>
26
27 #include "psppire-object.h"
28 #include "psppire-dict.h"
29 #include <data/format.h>
30 #include <data/dictionary.h>
31 #include <data/missing-values.h>
32 #include <data/value-labels.h>
33
34
35 #include "message-dialog.h"
36 #include "psppire-variable.h"
37
38 /* --- prototypes --- */
39 static void psppire_dict_class_init     (PsppireDictClass       *class);
40 static void psppire_dict_init   (PsppireDict            *dict);
41 static void psppire_dict_finalize       (GObject                *object);
42
43
44 /* --- variables --- */
45 static GObjectClass     *parent_class = NULL;
46
47 enum  {VARIABLE_CHANGED, 
48        VARIABLE_INSERTED,
49        VARIABLES_DELETED, 
50        n_SIGNALS};
51
52 static guint signal[n_SIGNALS];
53
54 #define CACHE_CHUNK 5
55
56 /* --- functions --- */
57 /**
58  * psppire_dict_get_type:
59  * @returns: the type ID for accelerator groups.
60  */
61 GType
62 psppire_dict_get_type (void)
63 {
64   static GType object_type = 0;
65
66   if (!object_type)
67     {
68       static const GTypeInfo object_info = {
69         sizeof (PsppireDictClass),
70         (GBaseInitFunc) NULL,
71         (GBaseFinalizeFunc) NULL,
72         (GClassInitFunc) psppire_dict_class_init,
73         NULL,   /* class_finalize */
74         NULL,   /* class_data */
75         sizeof (PsppireDict),
76         0,      /* n_preallocs */
77         (GInstanceInitFunc) psppire_dict_init,
78       };
79
80       object_type = g_type_register_static (G_TYPE_PSPPIRE_OBJECT, "PsppireDict",
81                                             &object_info, 0);
82     }
83
84   return object_type;
85 }
86
87
88 static void
89 psppire_dict_class_init (PsppireDictClass *class)
90 {
91   GObjectClass *object_class = G_OBJECT_CLASS (class);
92
93   parent_class = g_type_class_peek_parent (class);
94
95   object_class->finalize = psppire_dict_finalize;
96
97   signal[VARIABLE_CHANGED] =
98     g_signal_new ("variable_changed",
99                   G_TYPE_FROM_CLASS(class),
100                   G_SIGNAL_RUN_FIRST,
101                   0,
102                   NULL, NULL,
103                   g_cclosure_marshal_VOID__INT,
104                   G_TYPE_NONE, 
105                   1,
106                   G_TYPE_INT);
107
108
109
110   signal[VARIABLE_INSERTED] =
111     g_signal_new ("variable_inserted",
112                   G_TYPE_FROM_CLASS(class),
113                   G_SIGNAL_RUN_FIRST,
114                   0,
115                   NULL, NULL,
116                   g_cclosure_marshal_VOID__INT,
117                   G_TYPE_NONE, 
118                   1,
119                   G_TYPE_INT);
120
121
122   signal[VARIABLES_DELETED] =
123     g_signal_new ("variables_deleted",
124                   G_TYPE_FROM_CLASS(class),
125                   G_SIGNAL_RUN_FIRST,
126                   0,
127                   NULL, NULL,
128                   gtkextra_VOID__INT_INT,
129                   G_TYPE_NONE, 
130                   2,
131                   G_TYPE_INT,
132                   G_TYPE_INT);
133
134 }
135
136 static void
137 psppire_dict_finalize (GObject *object)
138 {
139   gint v;
140   PsppireDict *d = PSPPIRE_DICT (object);
141   
142   for (v = 0 ; v < psppire_dict_get_var_cnt(d) ; ++v ) 
143     g_free(d->variables[v]);
144
145   g_free(d->variables);
146   d->cache_size = 0;
147
148   dict_destroy(d->dict);
149
150   G_OBJECT_CLASS (parent_class)->finalize (object);
151 }
152
153 static void
154 psppire_dict_init (PsppireDict *psppire_dict)
155 {
156   psppire_dict->dict = dict_create();
157
158   psppire_dict->variables = 0; 
159   psppire_dict->cache_size = 0;
160 }
161
162 /**
163  * psppire_dict_new:
164  * @returns: a new #PsppireDict object
165  * 
166  * Creates a new #PsppireDict. 
167  */
168 PsppireDict*
169 psppire_dict_new (void)
170 {
171   return g_object_new (G_TYPE_PSPPIRE_DICT, NULL);
172 }
173
174
175 /**
176  * psppire_dict_new_from_dict:
177  * @returns: a new #PsppireDict object
178  * 
179  * Creates a new #PsppireDict. 
180  */
181 PsppireDict*
182 psppire_dict_new_from_dict (struct dictionary *d)
183 {
184   PsppireDict *new_dict = g_object_new (G_TYPE_PSPPIRE_DICT, NULL);
185   new_dict->dict = d;
186   new_dict->cache_size = dict_get_var_cnt(d);
187   new_dict->variables = g_malloc0(sizeof(struct PsppireVariable *) * 
188                                   new_dict->cache_size);
189
190
191   return new_dict;
192 }
193
194
195 /* Returns a valid name for a new variable in DICT.
196    The return value is statically allocated */
197 static gchar * 
198 auto_generate_var_name(PsppireDict *dict)
199 {
200   gint d = 0;
201   static gchar name[10];
202
203
204   while (g_snprintf(name, 10, "VAR%05d",d++),
205          psppire_dict_lookup_var(dict, name))
206     ;
207
208   return name;
209 }
210
211 /* Insert a new variable at posn IDX, with the name NAME.
212    If NAME is null, then a name will be automatically assigned.
213  */
214 void
215 psppire_dict_insert_variable(PsppireDict *d, gint idx, const gchar *name)
216 {
217   struct variable *var ;
218   gint i;
219   g_return_if_fail(d);
220   g_return_if_fail(G_IS_PSPPIRE_DICT(d));
221
222
223   /* Invalidate the cache from IDX onwards */
224   for ( i = idx ; i < d->cache_size ; ++i ) 
225     {
226       g_free(d->variables[i]);
227       d->variables[i] = 0;
228     }
229
230   /* Ensure that the cache is large enough */
231   if ( dict_get_var_cnt(d->dict) >= d->cache_size ) 
232     {
233       d->variables = g_realloc(d->variables, sizeof(struct PsppireVariable*) * 
234                                (d->cache_size + CACHE_CHUNK));
235       d->cache_size += CACHE_CHUNK;
236     }
237
238   /* Zero the new pointers */
239   for ( ; i < d->cache_size ; ++i ) 
240     {
241       d->variables[i] = 0;
242     }
243
244   if ( ! name ) 
245     name = auto_generate_var_name(d);
246   
247   var = dict_create_var(d->dict, name, 0);
248
249   dict_reorder_var(d->dict, var, idx);
250
251   d->variables[idx] = g_malloc(sizeof (struct PsppireVariable));
252   d->variables[idx]->v = var;
253   d->variables[idx]->dict = d;
254
255   g_signal_emit(d, signal[VARIABLE_INSERTED], 0, idx );  
256 }
257
258 /* Delete N variables beginning at FIRST */
259 void
260 psppire_dict_delete_variables(PsppireDict *d, gint first, gint n)
261 {
262   gint idx;
263   g_return_if_fail(d);
264   g_return_if_fail(d->dict);
265   g_return_if_fail(G_IS_PSPPIRE_DICT(d));
266
267   /* Invalidate all pvs from FIRST onwards */
268   for ( idx = first ; idx < d->cache_size ; ++idx ) 
269     {
270       g_free(d->variables[idx]);
271       d->variables[idx] = 0;
272     }
273
274   for (idx = 0 ; idx < n ; ++idx ) 
275     {
276       struct variable *var;
277
278       /* Do nothing if it's out of bounds */
279       if ( first >= dict_get_var_cnt (d->dict))
280         break; 
281
282       var = dict_get_var(d->dict, first);
283       dict_delete_var (d->dict, var);
284     }
285
286   g_signal_emit(d, signal[VARIABLES_DELETED], 0, first, idx );  
287 }
288
289
290 void
291 psppire_dict_set_name(PsppireDict* d, gint idx, const gchar *name)
292 {
293   struct variable *var;
294   g_assert(d);
295   g_assert(G_IS_PSPPIRE_DICT(d));
296
297
298   if ( idx < dict_get_var_cnt(d->dict))
299     {
300       /* This is an existing variable? */
301       var = dict_get_var(d->dict, idx);
302       dict_rename_var(d->dict, var, name);
303       g_signal_emit(d, signal[VARIABLE_CHANGED], 0, idx);
304     }
305   else
306     {
307       /* new variable */
308       dict_create_var(d->dict, name, 0);
309       g_print("Emitting variable-inserted signal\n");
310       g_signal_emit(d, signal[VARIABLE_INSERTED], 0, idx);
311     }
312 }
313
314
315
316 /* Return the IDXth variable */
317 struct PsppireVariable *
318 psppire_dict_get_variable(PsppireDict *d, gint idx)
319 {
320   struct PsppireVariable *var ;
321   g_return_val_if_fail(d, NULL);
322   g_return_val_if_fail(d->dict, NULL);
323
324   if ( ! d->variables) 
325     return NULL;
326   
327   if (idx < 0 || idx >= psppire_dict_get_var_cnt(d))
328     return NULL;
329
330   var = d->variables[idx] ; 
331
332   if (! var ) 
333     {
334       var = g_malloc(sizeof (*var));
335       var->dict = d;
336       var->v = dict_get_var(d->dict, idx);
337       d->variables[idx] = var;
338     }
339     
340   return var;
341 }
342
343
344 /* Return the number of variables in the dictionary */
345 gint 
346 psppire_dict_get_var_cnt(const PsppireDict *d)
347 {
348   g_return_val_if_fail(d, -1);
349   g_return_val_if_fail(d->dict, -1);
350   
351
352   return dict_get_var_cnt(d->dict);
353 }
354
355
356 /* Return a variable by name.
357    Return NULL if it doesn't exist
358 */
359 struct variable *
360 psppire_dict_lookup_var (const PsppireDict *d, const gchar *name)
361 {
362   g_return_val_if_fail(d, NULL);
363   g_return_val_if_fail(d->dict, NULL);
364
365   return dict_lookup_var(d->dict, name);
366 }
367
368
369 void
370 psppire_dict_var_changed(PsppireDict *d, gint idx)
371 {
372   g_return_if_fail(d);
373
374   g_signal_emit(d, signal[VARIABLE_CHANGED], 0, idx);
375 }
376
377
378 /* Clears the contents of D */
379 void 
380 psppire_dict_clear(PsppireDict *d)
381 {
382   g_return_if_fail(d);
383   g_return_if_fail(d->dict);
384
385   {
386     const gint n_vars = dict_get_var_cnt(d->dict);
387     gint i;
388   
389     dict_clear(d->dict);
390
391     /* Invalidate the entire cache */
392     for ( i = 0 ; i < d->cache_size ; ++i ) 
393       {
394         g_free(d->variables[i]);
395         d->variables[i] = 0;
396       }
397
398     g_signal_emit(d, signal[VARIABLES_DELETED], 0, 0, n_vars );  
399   }
400 }
401
402
403
404 /* Return true is NAME would be a valid name of a variable to add to the 
405    dictionary.  False otherwise. 
406    If REPORT is true, then invalid names will be reported as such as errors
407 */
408 gboolean
409 psppire_dict_check_name(const PsppireDict *dict, 
410                      const gchar *name, gboolean report)
411 {
412   if ( ! var_is_valid_name(name, report ) )
413       return FALSE;
414
415   if (psppire_dict_lookup_var(dict, name))
416     {
417       if ( report ) 
418         msg(ME,"Duplicate variable name.");
419       return FALSE;
420     }
421
422   return TRUE;
423 }