Fixed bug reporting the significance of paired value t-test.
[pspp-builds.git] / src / ui / gui / psppire-case-file.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2006  Free Software Foundation
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18 #include <string.h>
19 #include <stdlib.h>
20
21 #include "psppire-case-file.h"
22
23 #include <gtksheet/gtkextra-marshal.h>
24
25 #include <data/format.h>
26 #include <data/case.h>
27 #include <data/data-in.h>
28 #include <data/datasheet.h>
29 #include <data/casereader.h>
30 #include <math/sort.h>
31 #include <libpspp/misc.h>
32
33 #include "xalloc.h"
34 #include "xmalloca.h"
35
36 /* --- prototypes --- */
37 static void psppire_case_file_class_init        (PsppireCaseFileClass   *class);
38 static void psppire_case_file_init      (PsppireCaseFile        *case_file);
39 static void psppire_case_file_finalize  (GObject                *object);
40
41
42 /* --- variables --- */
43 static GObjectClass     *parent_class = NULL;
44
45 enum  {CASE_CHANGED,
46        CASE_INSERTED,
47        CASES_DELETED,
48        n_SIGNALS};
49
50 static guint signals [n_SIGNALS];
51
52
53 /* --- functions --- */
54 /**
55  * psppire_case_file_get_type:
56  * @returns: the type ID for accelerator groups.
57  */
58 GType
59 psppire_case_file_get_type (void)
60 {
61   static GType object_type = 0;
62
63   if (!object_type)
64     {
65       static const GTypeInfo object_info = {
66         sizeof (PsppireCaseFileClass),
67         (GBaseInitFunc) NULL,
68         (GBaseFinalizeFunc) NULL,
69         (GClassInitFunc) psppire_case_file_class_init,
70         NULL,   /* class_finalize */
71         NULL,   /* class_data */
72         sizeof (PsppireCaseFile),
73         0,      /* n_preallocs */
74         (GInstanceInitFunc) psppire_case_file_init,
75       };
76
77       object_type = g_type_register_static (G_TYPE_OBJECT, "PsppireCaseFile",
78                                             &object_info, 0);
79     }
80
81   return object_type;
82 }
83
84 /* Properties */
85 enum
86 {
87   PROP_0,
88   PROP_DATASHEET,
89   PROP_READER
90 };
91
92
93
94
95 static void
96 psppire_case_file_set_property (GObject         *object,
97                                 guint            prop_id,
98                                 const GValue    *value,
99                                 GParamSpec      *pspec)
100
101 {
102   PsppireCaseFile *cf = PSPPIRE_CASE_FILE (object);
103
104   switch (prop_id)
105     {
106     case PROP_READER:
107       cf->datasheet = datasheet_create (g_value_get_pointer (value));
108       cf->accessible = TRUE;
109       break;
110     default:
111       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
112       break;
113     };
114 }
115
116 static void
117 psppire_case_file_get_property (GObject         *object,
118                                 guint            prop_id,
119                                 GValue          *value,
120                                 GParamSpec      *pspec)
121 {
122   PsppireCaseFile *cf = PSPPIRE_CASE_FILE (object);
123
124   switch (prop_id)
125     {
126     case PROP_DATASHEET:
127       g_value_set_pointer (value, cf->datasheet);
128       break;
129     default:
130       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
131       break;
132     };
133 }
134
135
136 static void
137 psppire_case_file_class_init (PsppireCaseFileClass *class)
138 {
139   GObjectClass *object_class = G_OBJECT_CLASS (class);
140   GParamSpec *datasheet_spec ;
141   GParamSpec *reader_spec ;
142
143   parent_class = g_type_class_peek_parent (class);
144
145   object_class->finalize = psppire_case_file_finalize;
146
147   datasheet_spec =
148     g_param_spec_pointer ("datasheet",
149                           "Datasheet",
150                           "A pointer to the datasheet belonging to this object",
151                           G_PARAM_READABLE );
152   reader_spec =
153     g_param_spec_pointer ("casereader",
154                           "CaseReader",
155                           "A pointer to the case reader from which this object is constructed",
156                           G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE );
157
158   object_class->set_property = psppire_case_file_set_property;
159   object_class->get_property = psppire_case_file_get_property;
160
161   g_object_class_install_property (object_class,
162                                    PROP_DATASHEET,
163                                    datasheet_spec);
164
165   g_object_class_install_property (object_class,
166                                    PROP_READER,
167                                    reader_spec);
168
169   signals [CASE_CHANGED] =
170     g_signal_new ("case-changed",
171                   G_TYPE_FROM_CLASS (class),
172                   G_SIGNAL_RUN_FIRST,
173                   0,
174                   NULL, NULL,
175                   g_cclosure_marshal_VOID__INT,
176                   G_TYPE_NONE,
177                   1,
178                   G_TYPE_INT);
179
180
181   signals [CASE_INSERTED] =
182     g_signal_new ("case-inserted",
183                   G_TYPE_FROM_CLASS (class),
184                   G_SIGNAL_RUN_FIRST,
185                   0,
186                   NULL, NULL,
187                   g_cclosure_marshal_VOID__INT,
188                   G_TYPE_NONE,
189                   1,
190                   G_TYPE_INT);
191
192
193   signals [CASES_DELETED] =
194     g_signal_new ("cases-deleted",
195                   G_TYPE_FROM_CLASS (class),
196                   G_SIGNAL_RUN_FIRST,
197                   0,
198                   NULL, NULL,
199                   gtkextra_VOID__INT_INT,
200                   G_TYPE_NONE,
201                   2,
202                   G_TYPE_INT, G_TYPE_INT);
203 }
204
205 static void
206 psppire_case_file_finalize (GObject *object)
207 {
208   PsppireCaseFile *cf = PSPPIRE_CASE_FILE (object);
209
210   if ( cf->accessible)
211     datasheet_destroy (cf->datasheet);
212
213   G_OBJECT_CLASS (parent_class)->finalize (object);
214 }
215
216 static void
217 psppire_case_file_init (PsppireCaseFile *cf)
218 {
219   cf->datasheet = NULL;
220   cf->accessible = FALSE;
221 }
222
223
224 /**
225  * psppire_case_file_new:
226  * @returns: a new #PsppireCaseFile object
227  *
228  * Creates a new #PsppireCaseFile.
229  */
230 PsppireCaseFile*
231 psppire_case_file_new (struct casereader *reader)
232 {
233   return g_object_new (G_TYPE_PSPPIRE_CASE_FILE,
234                        "casereader", reader,
235                        NULL);
236 }
237
238
239 gboolean
240 psppire_case_file_delete_cases (PsppireCaseFile *cf, casenumber n_cases, casenumber first)
241 {
242   g_return_val_if_fail (cf, FALSE);
243   g_return_val_if_fail (cf->datasheet, FALSE);
244   g_return_val_if_fail (cf->accessible, FALSE);
245
246   g_return_val_if_fail (first + n_cases <=
247                         psppire_case_file_get_case_count (cf), FALSE);
248
249   datasheet_delete_rows (cf->datasheet, first, n_cases);
250
251   g_signal_emit (cf, signals [CASES_DELETED], 0, first, n_cases);
252
253   return TRUE;
254 }
255
256 /* Insert case CC into the case file before POSN */
257 gboolean
258 psppire_case_file_insert_case (PsppireCaseFile *cf,
259                                struct ccase *cc,
260                                casenumber posn)
261 {
262   struct ccase tmp;
263   bool result ;
264
265   g_return_val_if_fail (cf, FALSE);
266   g_return_val_if_fail (cf->datasheet, FALSE);
267   g_return_val_if_fail (cf->accessible, FALSE);
268
269   case_clone (&tmp, cc);
270   result = datasheet_insert_rows (cf->datasheet, posn, &tmp, 1);
271
272   if ( result )
273     g_signal_emit (cf, signals [CASE_INSERTED], 0, posn);
274   else
275     g_warning ("Cannot insert case at position %ld\n", posn);
276
277   return result;
278 }
279
280
281 casenumber
282 psppire_case_file_get_case_count (const PsppireCaseFile *cf)
283 {
284   g_return_val_if_fail (cf, FALSE);
285   g_return_val_if_fail (cf->accessible, FALSE);
286
287   if ( ! cf->datasheet)
288     return 0;
289
290   return datasheet_get_row_cnt (cf->datasheet);
291 }
292
293 /* Copies the IDXth value from case CASENUM into VALUE.
294    If VALUE is null, then memory is allocated is allocated with
295    malloc.  Returns the value if successful, NULL on failure. */
296 union value *
297 psppire_case_file_get_value (const PsppireCaseFile *cf,
298                              casenumber casenum, size_t idx,
299                              union value *value, int width)
300 {
301   bool allocated;
302
303   g_return_val_if_fail (cf, false);
304   g_return_val_if_fail (cf->datasheet, false);
305
306   g_return_val_if_fail (idx < datasheet_get_column_cnt (cf->datasheet), false);
307
308   if (value == NULL)
309     {
310       value = xnmalloc (value_cnt_from_width (width), sizeof *value);
311       allocated = true;
312     }
313   else
314     allocated = false;
315   if (!datasheet_get_value (cf->datasheet, casenum, idx, value, width))
316     {
317       if (allocated)
318         free (value);
319       value = NULL;
320     }
321   return value;
322 }
323
324 void
325 psppire_case_file_clear (PsppireCaseFile *cf)
326 {
327   datasheet_destroy (cf->datasheet);
328   cf->datasheet = NULL;
329   g_signal_emit (cf, signals [CASES_DELETED], 0, 0, -1);
330 }
331
332 /* Set the IDXth value of case C to V.
333    Returns true if successful, false on I/O error. */
334 gboolean
335 psppire_case_file_set_value (PsppireCaseFile *cf, casenumber casenum, gint idx,
336                             union value *v, gint width)
337 {
338   bool ok;
339
340   g_return_val_if_fail (cf, FALSE);
341   g_return_val_if_fail (cf->datasheet, FALSE);
342
343   g_return_val_if_fail (idx < datasheet_get_column_cnt (cf->datasheet), FALSE);
344
345   ok = datasheet_put_value (cf->datasheet, casenum, idx, v, width);
346   if (ok)
347     g_signal_emit (cf, signals [CASE_CHANGED], 0, casenum);
348   return ok;
349 }
350
351
352
353 /* Set the IDXth value of case C using D_IN */
354 gboolean
355 psppire_case_file_data_in (PsppireCaseFile *cf, casenumber casenum, gint idx,
356                           struct substring input, const struct fmt_spec *fmt)
357 {
358   union value *value = NULL;
359   int width;
360   bool ok;
361
362   g_return_val_if_fail (cf, FALSE);
363   g_return_val_if_fail (cf->datasheet, FALSE);
364
365   g_return_val_if_fail (idx < datasheet_get_column_cnt (cf->datasheet), FALSE);
366
367   width = fmt_var_width (fmt);
368   value = xmalloca (value_cnt_from_width (width) * sizeof *value);
369   ok = (datasheet_get_value (cf->datasheet, casenum, idx, value, width)
370         && data_in (input, LEGACY_NATIVE, fmt->type, 0, 0, 0, value, width)
371         && datasheet_put_value (cf->datasheet, casenum, idx, value, width));
372
373   if (ok)
374     g_signal_emit (cf, signals [CASE_CHANGED], 0, casenum);
375
376   freea (value);
377
378   return TRUE;
379 }
380
381
382 void
383 psppire_case_file_sort (PsppireCaseFile *cf, struct case_ordering *ordering)
384 {
385   struct casereader *sorted_data;
386   gint c;
387
388   sorted_data = sort_execute (datasheet_make_reader (cf->datasheet), ordering);
389   cf->datasheet = datasheet_create (sorted_data);
390
391   /* FIXME: Need to have a signal to change a range of cases, instead of
392      calling a signal many times */
393   for ( c = 0 ; c < datasheet_get_row_cnt (cf->datasheet) ; ++c )
394     g_signal_emit (cf, signals [CASE_CHANGED], 0, c);
395 }
396
397
398 /* Resize the cases in the casefile, by inserting N_VALUES into every
399    one of them at the position immediately preceeding WHERE.
400 */
401 gboolean
402 psppire_case_file_insert_values (PsppireCaseFile *cf,
403                                  gint n_values, gint where)
404 {
405   g_return_val_if_fail (cf, FALSE);
406   g_return_val_if_fail (cf->accessible, FALSE);
407
408   if ( n_values == 0 )
409     return FALSE;
410
411   g_assert (n_values > 0);
412
413   if ( ! cf->datasheet )
414     cf->datasheet = datasheet_create (NULL);
415
416   {
417     union value *values = xcalloc (n_values, sizeof *values);
418     datasheet_insert_columns (cf->datasheet, values, n_values, where);
419     free (values);
420   }
421
422   return TRUE;
423 }
424
425
426 /* Fills C with the CASENUMth case.
427    Returns true on success, false otherwise.
428  */
429 gboolean
430 psppire_case_file_get_case (const PsppireCaseFile *cf, casenumber casenum,
431                            struct ccase *c)
432 {
433   g_return_val_if_fail (cf, FALSE);
434   g_return_val_if_fail (cf->datasheet, FALSE);
435
436   return datasheet_get_row (cf->datasheet, casenum, c);
437 }
438
439
440
441 struct casereader *
442 psppire_case_file_make_reader (PsppireCaseFile *cf)
443 {
444   struct casereader *r = datasheet_make_reader (cf->datasheet);
445   cf->accessible = FALSE;
446   return r;
447 }
448