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