6c7942f551a0bfe34e86bd80c6d7b190c1098e8e
[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
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18     02110-1301, USA. */
19
20 #include <config.h>
21 #include <string.h>
22 #include <stdlib.h>
23
24 #include "psppire-object.h"
25 #include "psppire-case-file.h"
26
27 #include <gtksheet/gtkextra-marshal.h>
28
29 #include <data/case.h>
30 #include <ui/flexifile.h>
31 #include "flexifile-factory.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 /**
150  * psppire_case_file_new:
151  * @returns: a new #PsppireCaseFile object
152  *
153  * Creates a new #PsppireCaseFile.
154  */
155 PsppireCaseFile*
156 psppire_case_file_new (gint val_cnt)
157 {
158   PsppireCaseFile *cf = g_object_new (G_TYPE_PSPPIRE_CASE_FILE, NULL);
159
160   cf->flexifile = flexifile_create (val_cnt);
161
162   return cf;
163 }
164
165
166 /**
167  * psppire_case_file_new_from_flexifile:
168  * @returns: a new #PsppireCaseFile object
169  *
170  * Creates a new #PsppireCaseFile from an existing flexifile
171  */
172 PsppireCaseFile*
173 psppire_case_file_new_from_flexifile (struct flexifile *ff)
174 {
175   PsppireCaseFile *cf = g_object_new (G_TYPE_PSPPIRE_CASE_FILE, NULL);
176
177   cf->flexifile = (struct casefile *) ff;
178
179   return cf;
180 }
181
182
183 gboolean
184 psppire_case_file_delete_cases(PsppireCaseFile *cf, gint n_cases, gint first)
185 {
186   int result;
187
188   g_return_val_if_fail(cf, FALSE);
189   g_return_val_if_fail(cf->flexifile, FALSE);
190
191   result =  flexifile_delete_cases(FLEXIFILE(cf->flexifile), n_cases,  first);
192
193   g_signal_emit(cf, signal[CASES_DELETED], 0, n_cases, first);
194
195   return result;
196 }
197
198 /* Insert case CC into the case file before POSN */
199 gboolean
200 psppire_case_file_insert_case(PsppireCaseFile *cf,
201                               struct ccase *cc,
202                               gint posn)
203 {
204   bool result ;
205
206   g_return_val_if_fail(cf, FALSE);
207   g_return_val_if_fail(cf->flexifile, FALSE);
208
209   result = flexifile_insert_case(FLEXIFILE(cf->flexifile), cc, posn);
210
211   if ( result )
212     g_signal_emit(cf, signal[CASE_INSERTED], 0, posn);
213   else
214     g_warning("Cannot insert case at position %d\n", posn);
215
216   return result;
217 }
218
219
220 /* Append a case to the case file */
221 gboolean
222 psppire_case_file_append_case(PsppireCaseFile *cf,
223                               struct ccase *c)
224 {
225   bool result ;
226   gint posn ;
227
228   g_return_val_if_fail(cf, FALSE);
229   g_return_val_if_fail(cf->flexifile, FALSE);
230
231   posn = casefile_get_case_cnt(cf->flexifile);
232
233   result = casefile_append(cf->flexifile, c);
234
235   g_signal_emit(cf, signal[CASE_INSERTED], 0, posn);
236
237   return result;
238 }
239
240
241 inline gint
242 psppire_case_file_get_case_count(const PsppireCaseFile *cf)
243 {
244   g_return_val_if_fail(cf, FALSE);
245
246   if ( ! cf->flexifile)
247     return 0;
248
249   return casefile_get_case_cnt(cf->flexifile);
250 }
251
252 /* Return the IDXth value from case CASENUM.
253    The return value must not be freed or written to
254  */
255 const union value *
256 psppire_case_file_get_value(const PsppireCaseFile *cf, gint casenum, gint idx)
257 {
258   const union value *v;
259   struct ccase c;
260
261   g_return_val_if_fail(cf, NULL);
262   g_return_val_if_fail(cf->flexifile, NULL);
263
264   g_return_val_if_fail(idx < casefile_get_value_cnt(cf->flexifile), NULL);
265
266   flexifile_get_case(FLEXIFILE(cf->flexifile), casenum, &c);
267
268   v = case_data_idx(&c, idx);
269   case_destroy(&c);
270
271   return v;
272 }
273
274 void
275 psppire_case_file_clear(PsppireCaseFile *cf)
276 {
277   casefile_destroy(cf->flexifile);
278   cf->flexifile = 0;
279   g_signal_emit(cf, signal[CASES_DELETED], 0, 0, -1);
280 }
281
282 /* Set the IDXth value of case C to SYSMIS/EMPTY */
283 gboolean
284 psppire_case_file_set_value(PsppireCaseFile *cf, gint casenum, gint idx,
285                             union value *v, gint width)
286 {
287   struct ccase cc ;
288   int bytes;
289
290   g_return_val_if_fail(cf, FALSE);
291   g_return_val_if_fail(cf->flexifile, FALSE);
292
293   g_return_val_if_fail(idx < casefile_get_value_cnt(cf->flexifile), FALSE);
294
295   if ( ! flexifile_get_case(FLEXIFILE(cf->flexifile), casenum, &cc) )
296     return FALSE;
297
298   if ( width == 0 )
299     bytes = MAX_SHORT_STRING;
300   else
301     bytes = DIV_RND_UP(width, MAX_SHORT_STRING) * MAX_SHORT_STRING ;
302
303   /* Cast away const in flagrant abuse of the casefile */
304   memcpy((union value *)case_data_idx(&cc, idx), v, bytes);
305
306   g_signal_emit(cf, signal[CASE_CHANGED], 0, casenum);
307
308   return TRUE;
309 }
310
311
312
313 /* Set the IDXth value of case C using D_IN */
314 gboolean
315 psppire_case_file_data_in(PsppireCaseFile *cf, gint casenum, gint idx,
316                           struct substring input, const struct fmt_spec *fmt)
317 {
318   struct ccase cc ;
319
320   g_return_val_if_fail(cf, FALSE);
321   g_return_val_if_fail(cf->flexifile, FALSE);
322
323   g_return_val_if_fail(idx < casefile_get_value_cnt(cf->flexifile), FALSE);
324
325   if ( ! flexifile_get_case(FLEXIFILE(cf->flexifile), casenum, &cc) )
326     return FALSE;
327
328   /* Cast away const in flagrant abuse of the casefile */
329   if (!data_in (input, fmt->type, 0, 0,
330                 (union value *) case_data_idx(&cc, idx), fmt_var_width (fmt)))
331     g_warning("Cant set value\n");
332
333   g_signal_emit(cf, signal[CASE_CHANGED], 0, casenum);
334
335   return TRUE;
336 }
337
338
339 void
340 psppire_case_file_sort(PsppireCaseFile *cf, const struct sort_criteria *sc)
341 {
342   gint c;
343
344   struct casereader *reader = casefile_get_reader (cf->flexifile, NULL);
345   struct casefile *cfile;
346
347   struct casefile_factory *factory  = flexifile_factory_create ();
348
349   cfile = sort_execute(reader, sc, factory);
350
351   casefile_destroy(cf->flexifile);
352
353   cf->flexifile = cfile;
354
355   /* FIXME: Need to have a signal to change a range of cases, instead of
356      calling a signal many times */
357   for ( c = 0 ; c < casefile_get_case_cnt(cf->flexifile) ; ++c )
358     g_signal_emit(cf, signal[CASE_CHANGED], 0, c);
359
360   flexifile_factory_destroy (factory);
361 }
362
363
364 /* Resize the cases in the casefile, by inserting N_VALUES into every
365    one of them. */
366 gboolean
367 psppire_case_file_insert_values (PsppireCaseFile *cf,
368                                  gint n_values, gint before)
369 {
370   g_return_val_if_fail(cf, FALSE);
371
372   if ( ! cf->flexifile )
373     {
374       cf->flexifile = flexifile_create(n_values);
375
376       return TRUE;
377     }
378
379   return flexifile_resize (FLEXIFILE(cf->flexifile), n_values, before);
380 }
381
382 /* Fills C with the CASENUMth case.
383    Returns true on success, false otherwise.
384  */
385 gboolean
386 psppire_case_file_get_case (const PsppireCaseFile *cf, gint casenum,
387                            struct ccase *c)
388 {
389   g_return_val_if_fail (cf, FALSE);
390   g_return_val_if_fail (cf->flexifile, FALSE);
391
392   return flexifile_get_case (FLEXIFILE(cf->flexifile), casenum, c);
393 }