35db14ef9b6b2aa0eaf374700322afba56454d2b
[pspp] / 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 #include <config.h>
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 case CC into the case file before POSN */
181 gboolean
182 psppire_case_file_insert_case(PsppireCaseFile *cf, 
183                               struct ccase *cc,
184                               gint posn)
185 {
186   bool result ;
187
188   g_return_val_if_fail(cf, FALSE);
189   g_return_val_if_fail(cf->flexifile, FALSE);
190
191   result = flexifile_insert_case(FLEXIFILE(cf->flexifile), cc, posn);
192   
193   if ( result ) 
194     g_signal_emit(cf, signal[CASE_INSERTED], 0, posn);
195   else
196     g_warning("Cannot insert case at position %d\n", posn);
197                 
198   return result;
199 }
200
201
202 /* Append a case to the case file */
203 gboolean
204 psppire_case_file_append_case(PsppireCaseFile *cf, 
205                               struct ccase *c)
206 {
207   bool result ;
208   gint posn ;
209
210   g_return_val_if_fail(cf, FALSE);
211   g_return_val_if_fail(cf->flexifile, FALSE);
212
213   posn = casefile_get_case_cnt(cf->flexifile);
214
215   result = casefile_append(cf->flexifile, c);
216   
217   g_signal_emit(cf, signal[CASE_INSERTED], 0, posn);
218                 
219   return result;
220 }
221
222
223 inline gint
224 psppire_case_file_get_case_count(const PsppireCaseFile *cf)
225 {
226   g_return_val_if_fail(cf, FALSE);
227   
228   if ( ! cf->flexifile) 
229     return 0;
230
231   return casefile_get_case_cnt(cf->flexifile);
232 }
233
234 /* Return the IDXth value from case CASENUM.
235    The return value must not be freed or written to
236  */
237 const union value *
238 psppire_case_file_get_value(const PsppireCaseFile *cf, gint casenum, gint idx)
239 {
240   const union value *v; 
241   struct ccase c;
242
243   g_return_val_if_fail(cf, NULL);
244   g_return_val_if_fail(cf->flexifile, NULL);
245
246   g_return_val_if_fail(idx < casefile_get_value_cnt(cf->flexifile), NULL);
247
248   flexifile_get_case(FLEXIFILE(cf->flexifile), casenum, &c);
249
250   v = case_data_idx(&c, idx);
251
252   case_destroy(&c);
253
254   return v;
255 }
256
257 void
258 psppire_case_file_clear(PsppireCaseFile *cf)
259 {
260   casefile_destroy(cf->flexifile);
261   cf->flexifile = 0;
262   g_signal_emit(cf, signal[CASES_DELETED], 0, 0, -1);
263 }
264
265 /* Set the IDXth value of case C to SYSMIS/EMPTY */
266 gboolean
267 psppire_case_file_set_value(PsppireCaseFile *cf, gint casenum, gint idx,
268                             union value *v, gint width)
269 {
270   struct ccase cc ;
271   int bytes;
272
273   g_return_val_if_fail(cf, FALSE);
274   g_return_val_if_fail(cf->flexifile, FALSE);
275
276   g_return_val_if_fail(idx < casefile_get_value_cnt(cf->flexifile), FALSE);
277
278   if ( ! flexifile_get_case(FLEXIFILE(cf->flexifile), casenum, &cc) )
279     return FALSE;
280
281   if ( width == 0 ) 
282     bytes = MAX_SHORT_STRING;
283   else
284     bytes = DIV_RND_UP(width, MAX_SHORT_STRING) * MAX_SHORT_STRING ;
285
286   /* Cast away const in flagrant abuse of the casefile */
287   memcpy((union value *)case_data_idx(&cc, idx), v, bytes);
288
289   g_signal_emit(cf, signal[CASE_CHANGED], 0, casenum);
290
291   return TRUE;
292 }
293
294
295
296 /* Set the IDXth value of case C using D_IN */
297 gboolean
298 psppire_case_file_data_in(PsppireCaseFile *cf, gint casenum, gint idx,
299                           struct substring input, const struct fmt_spec *fmt)
300 {
301   struct ccase cc ;
302
303   g_return_val_if_fail(cf, FALSE);
304   g_return_val_if_fail(cf->flexifile, FALSE);
305
306   g_return_val_if_fail(idx < casefile_get_value_cnt(cf->flexifile), FALSE);
307
308   if ( ! flexifile_get_case(FLEXIFILE(cf->flexifile), casenum, &cc) )
309     return FALSE;
310
311   /* Cast away const in flagrant abuse of the casefile */
312   if (!data_in (input, fmt->type, 0, 0,
313                 (union value *) case_data_idx(&cc, idx), fmt_var_width (fmt)))
314     g_warning("Cant set value\n");
315
316   g_signal_emit(cf, signal[CASE_CHANGED], 0, casenum);
317
318   return TRUE;
319 }
320
321
322 void
323 psppire_case_file_sort(PsppireCaseFile *cf, const struct sort_criteria *sc)
324 {
325   struct ccase cc;
326   gint c;
327   struct casefile *cfile;
328   struct casereader *reader = casefile_get_reader (cf->flexifile, NULL);
329   const int value_cnt = casefile_get_value_cnt(cf->flexifile);
330
331   cfile = sort_execute(reader, sc);
332
333   casefile_destroy(cf->flexifile);
334
335   /* Copy casefile into flexifile */
336
337   reader = casefile_get_destructive_reader(cfile);
338   cf->flexifile = flexifile_create(value_cnt);
339   while(casereader_read(reader, &cc))
340       casefile_append(cf->flexifile, &cc);
341   
342
343   /* FIXME: Need to have a signal to change a range of cases, instead of
344      calling a signal many times */
345   for ( c = 0 ; c < casefile_get_case_cnt(cf->flexifile) ; ++c ) 
346     g_signal_emit(cf, signal[CASE_CHANGED], 0, c);
347 }
348
349
350 /* Resize the cases in the casefile, by inserting N_VALUES into every 
351    one of them. */
352 gboolean 
353 psppire_case_file_insert_values (PsppireCaseFile *cf, 
354                                  gint n_values, gint before)
355 {
356   g_return_val_if_fail(cf, FALSE);
357
358   if ( ! cf->flexifile ) 
359     {
360       cf->flexifile = flexifile_create(n_values);
361       return TRUE;
362     }
363
364   return flexifile_resize (FLEXIFILE(cf->flexifile), n_values, before);
365 }
366
367 /* Fills C with the CASENUMth case.
368    Returns true on success, false otherwise.
369  */
370 gboolean
371 psppire_case_file_get_case (const PsppireCaseFile *cf, gint casenum, 
372                            struct ccase *c)
373 {
374   g_return_val_if_fail (cf, FALSE);
375   g_return_val_if_fail (cf->flexifile, FALSE);
376
377   return flexifile_get_case (FLEXIFILE(cf->flexifile), casenum, c);
378 }