eef28a1fb0c1dded9f4fcbb9d4e1e88450f3c501
[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 inline casenumber
209 psppire_case_file_get_case_count (const PsppireCaseFile *cf)
210 {
211   g_return_val_if_fail (cf, FALSE);
212   g_return_val_if_fail (cf->accessible, FALSE);
213
214   if ( ! cf->datasheet)
215     return 0;
216
217   return datasheet_get_row_cnt (cf->datasheet);
218 }
219
220 /* Copies the IDXth value from case CASENUM into VALUE.
221    If VALUE is null, then memory is allocated is allocated with
222    malloc.  Returns the value if successful, NULL on failure. */
223 union value *
224 psppire_case_file_get_value (const PsppireCaseFile *cf,
225                              casenumber casenum, size_t idx,
226                              union value *value, int width)
227 {
228   bool allocated;
229
230   g_return_val_if_fail (cf, false);
231   g_return_val_if_fail (cf->datasheet, false);
232
233   g_return_val_if_fail (idx < datasheet_get_column_cnt (cf->datasheet), false);
234
235   if (value == NULL)
236     {
237       value = xnmalloc (value_cnt_from_width (width), sizeof *value);
238       allocated = true;
239     }
240   else
241     allocated = false;
242   if (!datasheet_get_value (cf->datasheet, casenum, idx, value, width))
243     {
244       if (allocated)
245         free (value);
246       value = NULL;
247     }
248   return value;
249 }
250
251 void
252 psppire_case_file_clear (PsppireCaseFile *cf)
253 {
254   datasheet_destroy (cf->datasheet);
255   cf->datasheet = NULL;
256   g_signal_emit (cf, signals [CASES_DELETED], 0, 0, -1);
257 }
258
259 /* Set the IDXth value of case C to V.
260    Returns true if successful, false on I/O error. */
261 gboolean
262 psppire_case_file_set_value (PsppireCaseFile *cf, casenumber casenum, gint idx,
263                             union value *v, gint width)
264 {
265   bool ok;
266
267   g_return_val_if_fail (cf, FALSE);
268   g_return_val_if_fail (cf->datasheet, FALSE);
269
270   g_return_val_if_fail (idx < datasheet_get_column_cnt (cf->datasheet), FALSE);
271
272   ok = datasheet_put_value (cf->datasheet, casenum, idx, v, width);
273   if (ok)
274     g_signal_emit (cf, signals [CASE_CHANGED], 0, casenum);
275   return ok;
276 }
277
278
279
280 /* Set the IDXth value of case C using D_IN */
281 gboolean
282 psppire_case_file_data_in (PsppireCaseFile *cf, casenumber casenum, gint idx,
283                           struct substring input, const struct fmt_spec *fmt)
284 {
285   union value *value;
286   int width;
287   bool ok;
288
289   g_return_val_if_fail (cf, FALSE);
290   g_return_val_if_fail (cf->datasheet, FALSE);
291
292   g_return_val_if_fail (idx < datasheet_get_column_cnt (cf->datasheet), FALSE);
293
294   width = fmt_var_width (fmt);
295   value = xmalloca (value_cnt_from_width (width) * sizeof *value);
296   ok = (datasheet_get_value (cf->datasheet, casenum, idx, value, width)
297         && data_in (input, fmt->type, 0, 0, value, width)
298         && datasheet_put_value (cf->datasheet, casenum, idx, value, width));
299
300   if (ok)
301     g_signal_emit (cf, signals [CASE_CHANGED], 0, casenum);
302
303   freea (value);
304
305   return TRUE;
306 }
307
308
309 void
310 psppire_case_file_sort (PsppireCaseFile *cf, struct case_ordering *ordering)
311 {
312   struct casereader *sorted_data;
313   gint c;
314
315   sorted_data = sort_execute (datasheet_make_reader (cf->datasheet), ordering);
316   cf->datasheet = datasheet_create (sorted_data);
317
318   /* FIXME: Need to have a signal to change a range of cases, instead of
319      calling a signal many times */
320   for ( c = 0 ; c < datasheet_get_row_cnt (cf->datasheet) ; ++c )
321     g_signal_emit (cf, signals [CASE_CHANGED], 0, c);
322 }
323
324
325 /* Resize the cases in the casefile, by inserting N_VALUES into every
326    one of them at the position immediately preceeding WHERE.
327 */
328 gboolean
329 psppire_case_file_insert_values (PsppireCaseFile *cf,
330                                  gint n_values, gint where)
331 {
332   g_return_val_if_fail (cf, FALSE);
333   g_return_val_if_fail (cf->accessible, FALSE);
334
335   if ( n_values == 0 )
336     return FALSE;
337
338   g_assert (n_values > 0);
339
340   if ( ! cf->datasheet )
341     cf->datasheet = datasheet_create (NULL);
342
343   {
344     union value *values = xcalloc (n_values, sizeof *values);
345     datasheet_insert_columns (cf->datasheet, values, n_values, where);
346   free (values);
347   }
348
349   return TRUE;
350 }
351
352
353 /* Fills C with the CASENUMth case.
354    Returns true on success, false otherwise.
355  */
356 gboolean
357 psppire_case_file_get_case (const PsppireCaseFile *cf, casenumber casenum,
358                            struct ccase *c)
359 {
360   g_return_val_if_fail (cf, FALSE);
361   g_return_val_if_fail (cf->datasheet, FALSE);
362
363   return datasheet_get_row (cf->datasheet, casenum, c);
364 }
365
366
367
368 struct casereader *
369 psppire_case_file_make_reader (PsppireCaseFile *cf)
370 {
371   struct casereader *r = datasheet_make_reader (cf->datasheet);
372   cf->accessible = FALSE;
373   return r;
374 }
375