Actually implement the new procedure code and adapt all of its clients
[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-case-file.h"
25
26 #include <gtksheet/gtkextra-marshal.h>
27
28 #include <data/case.h>
29 #include <data/data-in.h>
30 #include <data/datasheet.h>
31 #include <math/sort.h>
32 #include <libpspp/misc.h>
33
34 #include "xalloc.h"
35 #include "xallocsa.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 signals [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_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   signals [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   signals [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   signals [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   datasheet_destroy (cf->datasheet);
137
138   G_OBJECT_CLASS (parent_class)->finalize (object);
139 }
140
141 static void
142 psppire_case_file_init (PsppireCaseFile *cf)
143 {
144   cf->datasheet = NULL;
145 }
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 (void)
156 {
157   PsppireCaseFile *cf = g_object_new (G_TYPE_PSPPIRE_CASE_FILE, NULL);
158
159   cf->datasheet = datasheet_create (NULL);
160
161   return cf;
162 }
163
164
165 void
166 psppire_case_file_replace_datasheet (PsppireCaseFile *cf, struct datasheet *ds)
167 {
168   cf->datasheet = ds;
169 }
170
171
172
173 gboolean
174 psppire_case_file_delete_cases (PsppireCaseFile *cf, gint n_cases, gint first)
175 {
176   g_return_val_if_fail (cf, FALSE);
177   g_return_val_if_fail (cf->datasheet, FALSE);
178
179   datasheet_delete_rows (cf->datasheet, first, n_cases);
180
181   g_signal_emit (cf, signals [CASES_DELETED], 0, n_cases, first);
182
183   return TRUE;
184 }
185
186 /* Insert case CC into the case file before POSN */
187 gboolean
188 psppire_case_file_insert_case (PsppireCaseFile *cf,
189                               struct ccase *cc,
190                               gint posn)
191 {
192   struct ccase tmp;
193   bool result ;
194
195   g_return_val_if_fail (cf, FALSE);
196   g_return_val_if_fail (cf->datasheet, FALSE);
197
198   case_clone (&tmp, cc);
199   result = datasheet_insert_rows (cf->datasheet, posn, &tmp, 1);
200
201   if ( result )
202     g_signal_emit (cf, signals [CASE_INSERTED], 0, posn);
203   else
204     g_warning ("Cannot insert case at position %d\n", posn);
205
206   return result;
207 }
208
209
210 /* Append a case to the case file */
211 gboolean
212 psppire_case_file_append_case (PsppireCaseFile *cf,
213                               struct ccase *c)
214 {
215   struct ccase tmp;
216   bool result ;
217   gint posn ;
218
219   g_return_val_if_fail (cf, FALSE);
220   g_return_val_if_fail (cf->datasheet, FALSE);
221
222   posn = datasheet_get_row_cnt (cf->datasheet);
223
224   case_clone (&tmp, c);
225   result = datasheet_insert_rows (cf->datasheet, posn, &tmp, 1);
226
227   g_signal_emit (cf, signals [CASE_INSERTED], 0, posn);
228
229   return result;
230 }
231
232
233 inline gint
234 psppire_case_file_get_case_count (const PsppireCaseFile *cf)
235 {
236   g_return_val_if_fail (cf, FALSE);
237
238   if ( ! cf->datasheet)
239     return 0;
240
241   return datasheet_get_row_cnt (cf->datasheet);
242 }
243
244 /* Copies the IDXth value from case CASENUM into VALUE.
245    If VALUE is null, then memory is allocated is allocated with
246    malloc.  Returns the value if successful, NULL on failure. */
247 union value *
248 psppire_case_file_get_value (const PsppireCaseFile *cf,
249                              casenumber casenum, size_t idx,
250                              union value *value, int width)
251 {
252   bool allocated;
253   
254   g_return_val_if_fail (cf, false);
255   g_return_val_if_fail (cf->datasheet, false);
256
257   g_return_val_if_fail (idx < datasheet_get_column_cnt (cf->datasheet), false);
258
259   if (value == NULL) 
260     {
261       value = xnmalloc (value_cnt_from_width (width), sizeof *value);
262       allocated = true;
263     }
264   else
265     allocated = false;
266   if (!datasheet_get_value (cf->datasheet, casenum, idx, value, width))
267     {
268       if (allocated) 
269         free (value);
270       value = NULL;
271     }
272   return value;
273 }
274
275 void
276 psppire_case_file_clear (PsppireCaseFile *cf)
277 {
278   datasheet_destroy (cf->datasheet);
279   cf->datasheet = NULL;
280   g_signal_emit (cf, signals [CASES_DELETED], 0, 0, -1);
281 }
282
283 /* Set the IDXth value of case C to V.
284    Returns true if successful, false on I/O error. */
285 gboolean
286 psppire_case_file_set_value (PsppireCaseFile *cf, gint casenum, gint idx,
287                             union value *v, gint width)
288 {
289   bool ok;
290
291   g_return_val_if_fail (cf, FALSE);
292   g_return_val_if_fail (cf->datasheet, FALSE);
293
294   g_return_val_if_fail (idx < datasheet_get_column_cnt (cf->datasheet), FALSE);
295
296   ok = datasheet_put_value (cf->datasheet, casenum, idx, v, width);
297   if (ok)
298     g_signal_emit (cf, signals [CASE_CHANGED], 0, casenum);
299   return ok;
300 }
301
302
303
304 /* Set the IDXth value of case C using D_IN */
305 gboolean
306 psppire_case_file_data_in (PsppireCaseFile *cf, gint casenum, gint idx,
307                           struct substring input, const struct fmt_spec *fmt)
308 {
309   union value *value;
310   int width;
311   bool ok;
312
313   g_return_val_if_fail (cf, FALSE);
314   g_return_val_if_fail (cf->datasheet, FALSE);
315
316   g_return_val_if_fail (idx < datasheet_get_column_cnt (cf->datasheet), FALSE);
317
318   width = fmt_var_width (fmt);
319   value = xallocsa (value_cnt_from_width (width) * sizeof *value);
320   ok = (datasheet_get_value (cf->datasheet, casenum, idx, value, width)
321         && data_in (input, fmt->type, 0, 0, value, width)
322         && datasheet_put_value (cf->datasheet, casenum, idx, value, width));
323
324   if (ok)
325     g_signal_emit (cf, signals [CASE_CHANGED], 0, casenum);
326
327   freesa (value);
328
329   return TRUE;
330 }
331
332
333 void
334 psppire_case_file_sort (PsppireCaseFile *cf, struct case_ordering *ordering)
335 {
336   struct casereader *sorted_data;
337   gint c;
338
339   sorted_data = sort_execute (datasheet_make_reader (cf->datasheet), ordering);
340   cf->datasheet = datasheet_create (sorted_data);
341
342   /* FIXME: Need to have a signal to change a range of cases, instead of
343      calling a signal many times */
344   for ( c = 0 ; c < datasheet_get_row_cnt (cf->datasheet) ; ++c )
345     g_signal_emit (cf, signals [CASE_CHANGED], 0, c);
346 }
347
348
349 /* Resize the cases in the casefile, by inserting N_VALUES into every
350    one of them. */
351 gboolean
352 psppire_case_file_insert_values (PsppireCaseFile *cf,
353                                  gint n_values, gint before)
354 {
355   union value *values;
356   g_return_val_if_fail (cf, FALSE);
357
358   if ( ! cf->datasheet )
359     cf->datasheet = datasheet_create (NULL);
360
361   values = xcalloc (n_values, sizeof *values);
362   datasheet_insert_columns (cf->datasheet, values, n_values, before);
363   free (values);
364
365   return TRUE;
366 }
367
368 /* Fills C with the CASENUMth case.
369    Returns true on success, false otherwise.
370  */
371 gboolean
372 psppire_case_file_get_case (const PsppireCaseFile *cf, gint casenum,
373                            struct ccase *c)
374 {
375   g_return_val_if_fail (cf, FALSE);
376   g_return_val_if_fail (cf->datasheet, FALSE);
377
378   return datasheet_get_row (cf->datasheet, casenum, c);
379 }