215eecc51802be034fea0f7dfb50f4e99be65ca2
[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 <data/casereader.h>
32 #include <math/sort.h>
33 #include <libpspp/misc.h>
34
35 #include "xalloc.h"
36 #include "xmalloca.h"
37
38 /* --- prototypes --- */
39 static void psppire_case_file_class_init        (PsppireCaseFileClass   *class);
40 static void psppire_case_file_init      (PsppireCaseFile        *case_file);
41 static void psppire_case_file_finalize  (GObject                *object);
42
43
44 /* --- variables --- */
45 static GObjectClass     *parent_class = NULL;
46
47 enum  {CASE_CHANGED,
48        CASE_INSERTED,
49        CASES_DELETED,
50        n_SIGNALS};
51
52 static guint signals [n_SIGNALS];
53
54
55 /* --- functions --- */
56 /**
57  * psppire_case_file_get_type:
58  * @returns: the type ID for accelerator groups.
59  */
60 GType
61 psppire_case_file_get_type (void)
62 {
63   static GType object_type = 0;
64
65   if (!object_type)
66     {
67       static const GTypeInfo object_info = {
68         sizeof (PsppireCaseFileClass),
69         (GBaseInitFunc) NULL,
70         (GBaseFinalizeFunc) NULL,
71         (GClassInitFunc) psppire_case_file_class_init,
72         NULL,   /* class_finalize */
73         NULL,   /* class_data */
74         sizeof (PsppireCaseFile),
75         0,      /* n_preallocs */
76         (GInstanceInitFunc) psppire_case_file_init,
77       };
78
79       object_type = g_type_register_static (G_TYPE_OBJECT, "PsppireCaseFile",
80                                             &object_info, 0);
81     }
82
83   return object_type;
84 }
85
86
87 static void
88 psppire_case_file_class_init (PsppireCaseFileClass *class)
89 {
90   GObjectClass *object_class = G_OBJECT_CLASS (class);
91
92   parent_class = g_type_class_peek_parent (class);
93
94   object_class->finalize = psppire_case_file_finalize;
95
96   signals [CASE_CHANGED] =
97     g_signal_new ("case-changed",
98                   G_TYPE_FROM_CLASS (class),
99                   G_SIGNAL_RUN_FIRST,
100                   0,
101                   NULL, NULL,
102                   g_cclosure_marshal_VOID__INT,
103                   G_TYPE_NONE,
104                   1,
105                   G_TYPE_INT);
106
107
108   signals [CASE_INSERTED] =
109     g_signal_new ("case-inserted",
110                   G_TYPE_FROM_CLASS (class),
111                   G_SIGNAL_RUN_FIRST,
112                   0,
113                   NULL, NULL,
114                   g_cclosure_marshal_VOID__INT,
115                   G_TYPE_NONE,
116                   1,
117                   G_TYPE_INT);
118
119
120   signals [CASES_DELETED] =
121     g_signal_new ("cases-deleted",
122                   G_TYPE_FROM_CLASS (class),
123                   G_SIGNAL_RUN_FIRST,
124                   0,
125                   NULL, NULL,
126                   gtkextra_VOID__INT_INT,
127                   G_TYPE_NONE,
128                   2,
129                   G_TYPE_INT, G_TYPE_INT);
130 }
131
132 static void
133 psppire_case_file_finalize (GObject *object)
134 {
135   PsppireCaseFile *cf = PSPPIRE_CASE_FILE (object);
136
137   if ( cf->accessible)
138     datasheet_destroy (cf->datasheet);
139
140   G_OBJECT_CLASS (parent_class)->finalize (object);
141 }
142
143 static void
144 psppire_case_file_init (PsppireCaseFile *cf)
145 {
146   cf->datasheet = NULL;
147   cf->accessible = FALSE;
148 }
149
150
151 /**
152  * psppire_case_file_new:
153  * @returns: a new #PsppireCaseFile object
154  *
155  * Creates a new #PsppireCaseFile.
156  */
157 PsppireCaseFile*
158 psppire_case_file_new (const struct casereader *reader)
159 {
160   PsppireCaseFile *cf = g_object_new (G_TYPE_PSPPIRE_CASE_FILE, NULL);
161
162   cf->datasheet = datasheet_create (casereader_clone (reader));
163   cf->accessible = TRUE;
164
165   return cf;
166 }
167
168
169 gboolean
170 psppire_case_file_delete_cases (PsppireCaseFile *cf, gint n_cases, gint first)
171 {
172   g_return_val_if_fail (cf, FALSE);
173   g_return_val_if_fail (cf->datasheet, FALSE);
174
175   datasheet_delete_rows (cf->datasheet, first, n_cases);
176
177   g_signal_emit (cf, signals [CASES_DELETED], 0, n_cases, first);
178
179   return TRUE;
180 }
181
182 /* Insert case CC into the case file before POSN */
183 gboolean
184 psppire_case_file_insert_case (PsppireCaseFile *cf,
185                               struct ccase *cc,
186                               gint posn)
187 {
188   struct ccase tmp;
189   bool result ;
190
191   g_return_val_if_fail (cf, FALSE);
192   g_return_val_if_fail (cf->datasheet, FALSE);
193
194   case_clone (&tmp, cc);
195   result = datasheet_insert_rows (cf->datasheet, posn, &tmp, 1);
196
197   if ( result )
198     g_signal_emit (cf, signals [CASE_INSERTED], 0, posn);
199   else
200     g_warning ("Cannot insert case at position %d\n", posn);
201
202   return result;
203 }
204
205
206 /* Append a case to the case file */
207 gboolean
208 psppire_case_file_append_case (PsppireCaseFile *cf,
209                               struct ccase *c)
210 {
211   struct ccase tmp;
212   bool result ;
213   gint posn ;
214
215   g_return_val_if_fail (cf, FALSE);
216   g_return_val_if_fail (cf->datasheet, FALSE);
217
218   posn = datasheet_get_row_cnt (cf->datasheet);
219
220   case_clone (&tmp, c);
221   result = datasheet_insert_rows (cf->datasheet, posn, &tmp, 1);
222
223   g_signal_emit (cf, signals [CASE_INSERTED], 0, posn);
224
225   return result;
226 }
227
228
229 inline gint
230 psppire_case_file_get_case_count (const PsppireCaseFile *cf)
231 {
232   g_return_val_if_fail (cf, FALSE);
233   g_return_val_if_fail (cf->accessible, FALSE);
234
235   if ( ! cf->datasheet)
236     return 0;
237
238   return datasheet_get_row_cnt (cf->datasheet);
239 }
240
241 /* Copies the IDXth value from case CASENUM into VALUE.
242    If VALUE is null, then memory is allocated is allocated with
243    malloc.  Returns the value if successful, NULL on failure. */
244 union value *
245 psppire_case_file_get_value (const PsppireCaseFile *cf,
246                              casenumber casenum, size_t idx,
247                              union value *value, int width)
248 {
249   bool allocated;
250
251   g_return_val_if_fail (cf, false);
252   g_return_val_if_fail (cf->datasheet, false);
253
254   g_return_val_if_fail (idx < datasheet_get_column_cnt (cf->datasheet), false);
255
256   if (value == NULL)
257     {
258       value = xnmalloc (value_cnt_from_width (width), sizeof *value);
259       allocated = true;
260     }
261   else
262     allocated = false;
263   if (!datasheet_get_value (cf->datasheet, casenum, idx, value, width))
264     {
265       if (allocated)
266         free (value);
267       value = NULL;
268     }
269   return value;
270 }
271
272 void
273 psppire_case_file_clear (PsppireCaseFile *cf)
274 {
275   datasheet_destroy (cf->datasheet);
276   cf->datasheet = NULL;
277   g_signal_emit (cf, signals [CASES_DELETED], 0, 0, -1);
278 }
279
280 /* Set the IDXth value of case C to V.
281    Returns true if successful, false on I/O error. */
282 gboolean
283 psppire_case_file_set_value (PsppireCaseFile *cf, gint casenum, gint idx,
284                             union value *v, gint width)
285 {
286   bool ok;
287
288   g_return_val_if_fail (cf, FALSE);
289   g_return_val_if_fail (cf->datasheet, FALSE);
290
291   g_return_val_if_fail (idx < datasheet_get_column_cnt (cf->datasheet), FALSE);
292
293   ok = datasheet_put_value (cf->datasheet, casenum, idx, v, width);
294   if (ok)
295     g_signal_emit (cf, signals [CASE_CHANGED], 0, casenum);
296   return ok;
297 }
298
299
300
301 /* Set the IDXth value of case C using D_IN */
302 gboolean
303 psppire_case_file_data_in (PsppireCaseFile *cf, gint casenum, gint idx,
304                           struct substring input, const struct fmt_spec *fmt)
305 {
306   union value *value;
307   int width;
308   bool ok;
309
310   g_return_val_if_fail (cf, FALSE);
311   g_return_val_if_fail (cf->datasheet, FALSE);
312
313   g_return_val_if_fail (idx < datasheet_get_column_cnt (cf->datasheet), FALSE);
314
315   width = fmt_var_width (fmt);
316   value = xmalloca (value_cnt_from_width (width) * sizeof *value);
317   ok = (datasheet_get_value (cf->datasheet, casenum, idx, value, width)
318         && data_in (input, fmt->type, 0, 0, value, width)
319         && datasheet_put_value (cf->datasheet, casenum, idx, value, width));
320
321   if (ok)
322     g_signal_emit (cf, signals [CASE_CHANGED], 0, casenum);
323
324   freea (value);
325
326   return TRUE;
327 }
328
329
330 void
331 psppire_case_file_sort (PsppireCaseFile *cf, struct case_ordering *ordering)
332 {
333   struct casereader *sorted_data;
334   gint c;
335
336   sorted_data = sort_execute (datasheet_make_reader (cf->datasheet), ordering);
337   cf->datasheet = datasheet_create (sorted_data);
338
339   /* FIXME: Need to have a signal to change a range of cases, instead of
340      calling a signal many times */
341   for ( c = 0 ; c < datasheet_get_row_cnt (cf->datasheet) ; ++c )
342     g_signal_emit (cf, signals [CASE_CHANGED], 0, c);
343 }
344
345
346 /* Resize the cases in the casefile, by inserting N_VALUES into every
347    one of them. */
348 gboolean
349 psppire_case_file_insert_values (PsppireCaseFile *cf,
350                                  gint n_values, gint before)
351 {
352   union value *values;
353   g_return_val_if_fail (cf, FALSE);
354   g_return_val_if_fail (cf->accessible, FALSE);
355
356   if ( ! cf->datasheet )
357     cf->datasheet = datasheet_create (NULL);
358
359   values = xcalloc (n_values, sizeof *values);
360   datasheet_insert_columns (cf->datasheet, values, n_values, before);
361   free (values);
362
363   return TRUE;
364 }
365
366 /* Fills C with the CASENUMth case.
367    Returns true on success, false otherwise.
368  */
369 gboolean
370 psppire_case_file_get_case (const PsppireCaseFile *cf, gint casenum,
371                            struct ccase *c)
372 {
373   g_return_val_if_fail (cf, FALSE);
374   g_return_val_if_fail (cf->datasheet, FALSE);
375
376   return datasheet_get_row (cf->datasheet, casenum, c);
377 }
378
379
380
381 struct casereader *
382 psppire_case_file_make_reader (PsppireCaseFile *cf)
383 {
384   struct casereader *r = datasheet_make_reader (cf->datasheet);
385   cf->accessible = FALSE;
386   return r;
387 }
388