Patch #5209
[pspp-builds.git] / src / ui / gui / psppire-case-file.c
1 /* 
2     PSPPIRE --- A Graphical User Interface for PSPP
3     Copyright (C) 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 "psppire-object.h"
26 #include "psppire-case-file.h"
27
28 #include <gtksheet/gtkextra-marshal.h>
29
30 #include <data/case.h>
31 #include <ui/flexifile.h>
32 #include <data/casefile.h>
33 #include <data/data-in.h>
34 #include <math/sort.h>
35 #include <libpspp/misc.h>
36
37 /* --- prototypes --- */
38 static void psppire_case_file_class_init        (PsppireCaseFileClass   *class);
39 static void psppire_case_file_init      (PsppireCaseFile        *case_file);
40 static void psppire_case_file_finalize  (GObject                *object);
41
42
43 /* --- variables --- */
44 static GObjectClass     *parent_class = NULL;
45
46 enum  {CASE_CHANGED, 
47        CASE_INSERTED,
48        CASES_DELETED,
49        n_SIGNALS};
50
51 static guint signal[n_SIGNALS];
52
53
54 /* --- functions --- */
55 /**
56  * psppire_case_file_get_type:
57  * @returns: the type ID for accelerator groups.
58  */
59 GType
60 psppire_case_file_get_type (void)
61 {
62   static GType object_type = 0;
63
64   if (!object_type)
65     {
66       static const GTypeInfo object_info = {
67         sizeof (PsppireCaseFileClass),
68         (GBaseInitFunc) NULL,
69         (GBaseFinalizeFunc) NULL,
70         (GClassInitFunc) psppire_case_file_class_init,
71         NULL,   /* class_finalize */
72         NULL,   /* class_data */
73         sizeof (PsppireCaseFile),
74         0,      /* n_preallocs */
75         (GInstanceInitFunc) psppire_case_file_init,
76       };
77
78       object_type = g_type_register_static (G_TYPE_PSPPIRE_OBJECT, "PsppireCaseFile",
79                                             &object_info, 0);
80     }
81
82   return object_type;
83 }
84
85
86 static void
87 psppire_case_file_class_init (PsppireCaseFileClass *class)
88 {
89   GObjectClass *object_class = G_OBJECT_CLASS (class);
90
91   parent_class = g_type_class_peek_parent (class);
92
93   object_class->finalize = psppire_case_file_finalize;
94
95   signal[CASE_CHANGED] =
96     g_signal_new ("case_changed",
97                   G_TYPE_FROM_CLASS(class),
98                   G_SIGNAL_RUN_FIRST,
99                   0,
100                   NULL, NULL,
101                   g_cclosure_marshal_VOID__INT,
102                   G_TYPE_NONE, 
103                   1,
104                   G_TYPE_INT);
105
106
107   signal[CASE_INSERTED] =
108     g_signal_new ("case_inserted",
109                   G_TYPE_FROM_CLASS(class),
110                   G_SIGNAL_RUN_FIRST,
111                   0,
112                   NULL, NULL,
113                   g_cclosure_marshal_VOID__INT,
114                   G_TYPE_NONE, 
115                   1,
116                   G_TYPE_INT);
117
118
119   signal[CASES_DELETED] =
120     g_signal_new ("cases_deleted",
121                   G_TYPE_FROM_CLASS(class),
122                   G_SIGNAL_RUN_FIRST,
123                   0,
124                   NULL, NULL,
125                   gtkextra_VOID__INT_INT,
126                   G_TYPE_NONE, 
127                   2,
128                   G_TYPE_INT, G_TYPE_INT);
129 }
130
131 static void
132 psppire_case_file_finalize (GObject *object)
133 {
134   PsppireCaseFile *cf = PSPPIRE_CASE_FILE (object);
135   
136   if ( cf->flexifile) 
137     casefile_destroy(cf->flexifile);
138
139   G_OBJECT_CLASS (parent_class)->finalize (object);
140 }
141
142 static void
143 psppire_case_file_init (PsppireCaseFile *cf)
144 {
145   cf->flexifile = 0;
146 }
147
148 /**
149  * psppire_case_file_new:
150  * @returns: a new #PsppireCaseFile object
151  * 
152  * Creates a new #PsppireCaseFile. 
153  */
154 PsppireCaseFile*
155 psppire_case_file_new (gint val_cnt)
156 {
157   PsppireCaseFile *cf = g_object_new (G_TYPE_PSPPIRE_CASE_FILE, NULL);
158
159   cf->flexifile = flexifile_create(val_cnt);
160
161   return cf;
162 }
163
164
165 gboolean
166 psppire_case_file_delete_cases(PsppireCaseFile *cf, gint n_cases, gint first)
167 {
168   int result;
169
170   g_return_val_if_fail(cf, FALSE);
171   g_return_val_if_fail(cf->flexifile, FALSE);
172
173   result =  flexifile_delete_cases(FLEXIFILE(cf->flexifile), n_cases,  first);
174
175   g_signal_emit(cf, signal[CASES_DELETED], 0, n_cases, first);
176
177   return result;
178 }
179
180 /* Insert a blank case to the case file */
181 gboolean
182 psppire_case_file_insert_case(PsppireCaseFile *cf, 
183                                gint posn)
184 {
185   bool result ;
186   struct ccase cc;
187
188   g_return_val_if_fail(cf, FALSE);
189   g_return_val_if_fail(cf->flexifile, FALSE);
190
191   case_create (&cc, casefile_get_value_cnt(cf->flexifile));
192
193   result = flexifile_insert_case(FLEXIFILE(cf->flexifile), &cc, posn);
194   
195   case_destroy (&cc);
196
197   if ( result ) 
198     g_signal_emit(cf, signal[CASE_INSERTED], 0, posn);
199   else
200     g_warning("Cannot insert case at position %d\n", posn);
201                 
202   return result;
203 }
204
205 /* Append a case to the case file */
206 gboolean
207 psppire_case_file_append_case(PsppireCaseFile *cf, 
208                               struct ccase *c)
209 {
210   bool result ;
211   gint posn ;
212
213   g_return_val_if_fail(cf, FALSE);
214   g_return_val_if_fail(cf->flexifile, FALSE);
215
216   posn = casefile_get_case_cnt(cf->flexifile);
217
218   result = casefile_append(cf->flexifile, c);
219   
220   g_signal_emit(cf, signal[CASE_INSERTED], 0, posn);
221                 
222   return result;
223 }
224
225
226 inline gint
227 psppire_case_file_get_case_count(const PsppireCaseFile *cf)
228 {
229   g_return_val_if_fail(cf, FALSE);
230   
231   if ( ! cf->flexifile) 
232     return 0;
233
234   return casefile_get_case_cnt(cf->flexifile);
235 }
236
237 /* Return the IDXth value from case CASENUM.
238    The return value must not be freed or written to
239  */
240 const union value *
241 psppire_case_file_get_value(const PsppireCaseFile *cf, gint casenum, gint idx)
242 {
243   const union value *v; 
244   struct ccase c;
245
246   g_return_val_if_fail(cf, NULL);
247   g_return_val_if_fail(cf->flexifile, NULL);
248
249   g_return_val_if_fail(idx < casefile_get_value_cnt(cf->flexifile), NULL);
250
251   flexifile_get_case(FLEXIFILE(cf->flexifile), casenum, &c);
252
253   v = case_data(&c, idx);
254
255   case_destroy(&c);
256
257   return v;
258 }
259
260 void
261 psppire_case_file_clear(PsppireCaseFile *cf)
262 {
263   casefile_destroy(cf->flexifile);
264   cf->flexifile = 0;
265   g_signal_emit(cf, signal[CASES_DELETED], 0, 0, -1);
266 }
267
268 /* Set the IDXth value of case C to SYSMIS/EMPTY */
269 gboolean
270 psppire_case_file_set_value(PsppireCaseFile *cf, gint casenum, gint idx,
271                             union value *v, gint width)
272 {
273   struct ccase cc ;
274   int bytes;
275
276   g_return_val_if_fail(cf, FALSE);
277   g_return_val_if_fail(cf->flexifile, FALSE);
278
279   g_return_val_if_fail(idx < casefile_get_value_cnt(cf->flexifile), FALSE);
280
281   if ( ! flexifile_get_case(FLEXIFILE(cf->flexifile), casenum, &cc) )
282     return FALSE;
283
284   if ( width == 0 ) 
285     bytes = MAX_SHORT_STRING;
286   else
287     bytes = DIV_RND_UP(width, MAX_SHORT_STRING) * MAX_SHORT_STRING ;
288
289   /* Cast away const in flagrant abuse of the casefile */
290   memcpy((union value *)case_data(&cc, idx), v, bytes);
291
292   g_signal_emit(cf, signal[CASE_CHANGED], 0, casenum);
293
294   return TRUE;
295 }
296
297
298
299 /* Set the IDXth value of case C using D_IN */
300 gboolean
301 psppire_case_file_data_in(PsppireCaseFile *cf, gint casenum, gint idx,
302                             struct data_in *d_in)
303 {
304   struct ccase cc ;
305
306   g_return_val_if_fail(cf, FALSE);
307   g_return_val_if_fail(cf->flexifile, FALSE);
308
309   g_return_val_if_fail(idx < casefile_get_value_cnt(cf->flexifile), FALSE);
310
311   if ( ! flexifile_get_case(FLEXIFILE(cf->flexifile), casenum, &cc) )
312     return FALSE;
313
314   /* Cast away const in flagrant abuse of the casefile */
315   d_in->v = (union value *) case_data(&cc, idx);
316
317   if ( ! data_in(d_in) ) 
318     g_warning("Cant set value\n");
319
320   g_signal_emit(cf, signal[CASE_CHANGED], 0, casenum);
321
322   return TRUE;
323 }
324
325
326 void
327 psppire_case_file_sort(PsppireCaseFile *cf, const struct sort_criteria *sc)
328 {
329   struct ccase cc;
330   gint c;
331   struct casefile *cfile;
332   struct casereader *reader = casefile_get_reader(cf->flexifile);
333   const int value_cnt = casefile_get_value_cnt(cf->flexifile);
334
335   cfile = sort_execute(reader, sc);
336
337   casefile_destroy(cf->flexifile);
338
339   /* Copy casefile into flexifile */
340
341   reader = casefile_get_destructive_reader(cfile);
342   cf->flexifile = flexifile_create(value_cnt);
343   while(casereader_read(reader, &cc))
344       casefile_append(cf->flexifile, &cc);
345   
346
347   /* FIXME: Need to have a signal to change a range of cases, instead of
348      calling a signal many times */
349   for ( c = 0 ; c < casefile_get_case_cnt(cf->flexifile) ; ++c ) 
350     g_signal_emit(cf, signal[CASE_CHANGED], 0, c);
351 }
352
353
354 /* Resize the cases in the casefile, by inserting N_VALUES into every 
355    one of them. */
356 gboolean 
357 psppire_case_file_insert_values(PsppireCaseFile *cf, 
358                                 gint n_values, gint before)
359 {
360   g_return_val_if_fail(cf, FALSE);
361
362   if ( ! cf->flexifile ) 
363     {
364       cf->flexifile = flexifile_create(n_values);
365       return TRUE;
366     }
367
368   return flexifile_resize(FLEXIFILE(cf->flexifile), n_values, before);
369 }
370