Fixed the two biggest sources of GtkCritical errors.
[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, casenumber n_cases, casenumber first)
168 {
169   g_return_val_if_fail (cf, FALSE);
170   g_return_val_if_fail (cf->datasheet, FALSE);
171   g_return_val_if_fail (cf->accessible, FALSE);
172
173   g_return_val_if_fail (first + n_cases <=
174                         psppire_case_file_get_case_count (cf), FALSE);
175
176   datasheet_delete_rows (cf->datasheet, first, n_cases);
177
178   g_signal_emit (cf, signals [CASES_DELETED], 0, first, n_cases);
179
180   return TRUE;
181 }
182
183 /* Insert case CC into the case file before POSN */
184 gboolean
185 psppire_case_file_insert_case (PsppireCaseFile *cf,
186                                struct ccase *cc,
187                                casenumber posn)
188 {
189   struct ccase tmp;
190   bool result ;
191
192   g_return_val_if_fail (cf, FALSE);
193   g_return_val_if_fail (cf->datasheet, FALSE);
194   g_return_val_if_fail (cf->accessible, FALSE);
195
196   case_clone (&tmp, cc);
197   result = datasheet_insert_rows (cf->datasheet, posn, &tmp, 1);
198
199   if ( result )
200     g_signal_emit (cf, signals [CASE_INSERTED], 0, posn);
201   else
202     g_warning ("Cannot insert case at position %ld\n", posn);
203
204   return result;
205 }
206
207
208 /* Append a case to the case file */
209 static gboolean
210 psppire_case_file_append_case (PsppireCaseFile *cf,
211                               struct ccase *c)
212 {
213   struct ccase tmp;
214   bool result ;
215   gint posn ;
216
217   g_return_val_if_fail (cf, FALSE);
218   g_return_val_if_fail (cf->datasheet, FALSE);
219   g_return_val_if_fail (cf->accessible, FALSE);
220
221   posn = datasheet_get_row_cnt (cf->datasheet);
222
223   case_clone (&tmp, c);
224   result = datasheet_insert_rows (cf->datasheet, posn, &tmp, 1);
225
226   g_signal_emit (cf, signals [CASE_INSERTED], 0, posn);
227
228   return result;
229 }
230
231
232 inline casenumber
233 psppire_case_file_get_case_count (const PsppireCaseFile *cf)
234 {
235   g_return_val_if_fail (cf, FALSE);
236   g_return_val_if_fail (cf->accessible, 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, casenumber 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, casenumber 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 = xmalloca (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   freea (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 at the position immediately preceeding WHERE.
351 */
352 gboolean
353 psppire_case_file_insert_values (PsppireCaseFile *cf,
354                                  gint n_values, gint where)
355 {
356   g_return_val_if_fail (cf, FALSE);
357   g_return_val_if_fail (cf->accessible, FALSE);
358
359   if ( n_values == 0 )
360     return FALSE;
361
362   g_assert (n_values > 0);
363
364   if ( ! cf->datasheet )
365     cf->datasheet = datasheet_create (NULL);
366
367   {
368     union value *values = xcalloc (n_values, sizeof *values);
369     datasheet_insert_columns (cf->datasheet, values, n_values, where);
370   free (values);
371   }
372
373   return TRUE;
374 }
375
376
377 /* Fills C with the CASENUMth case.
378    Returns true on success, false otherwise.
379  */
380 gboolean
381 psppire_case_file_get_case (const PsppireCaseFile *cf, casenumber casenum,
382                            struct ccase *c)
383 {
384   g_return_val_if_fail (cf, FALSE);
385   g_return_val_if_fail (cf->datasheet, FALSE);
386
387   return datasheet_get_row (cf->datasheet, casenum, c);
388 }
389
390
391
392 struct casereader *
393 psppire_case_file_make_reader (PsppireCaseFile *cf)
394 {
395   struct casereader *r = datasheet_make_reader (cf->datasheet);
396   cf->accessible = FALSE;
397   return r;
398 }
399