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