08503ff96ec755a952fd247077ea5f8bde4da7e6
[pspp] / 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 /* Properties */
84 enum
85 {
86   PROP_0,
87   PROP_DATASHEET,
88   PROP_READER
89 };
90
91
92
93
94 static void
95 psppire_case_file_set_property (GObject         *object,
96                                 guint            prop_id,
97                                 const GValue    *value,
98                                 GParamSpec      *pspec)
99
100 {
101   PsppireCaseFile *cf = PSPPIRE_CASE_FILE (object);
102
103   switch (prop_id)
104     {
105     case PROP_READER:
106       cf->datasheet = datasheet_create (g_value_get_pointer (value));
107       cf->accessible = TRUE;
108       break;
109     default:
110       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
111       break;
112     };
113 }
114
115 static void
116 psppire_case_file_get_property (GObject         *object,
117                                 guint            prop_id,
118                                 GValue          *value,
119                                 GParamSpec      *pspec)
120 {
121   PsppireCaseFile *cf = PSPPIRE_CASE_FILE (object);
122
123   switch (prop_id)
124     {
125     case PROP_DATASHEET:
126       g_value_set_pointer (value, cf->datasheet);
127       break;
128     default:
129       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
130       break;
131     };
132 }
133
134
135 static void
136 psppire_case_file_class_init (PsppireCaseFileClass *class)
137 {
138   GObjectClass *object_class = G_OBJECT_CLASS (class);
139   GParamSpec *datasheet_spec ;
140   GParamSpec *reader_spec ;
141
142   parent_class = g_type_class_peek_parent (class);
143
144   object_class->finalize = psppire_case_file_finalize;
145
146   datasheet_spec =
147     g_param_spec_pointer ("datasheet",
148                           "Datasheet",
149                           "A pointer to the datasheet belonging to this object",
150                           G_PARAM_READABLE );
151   reader_spec =
152     g_param_spec_pointer ("casereader",
153                           "CaseReader",
154                           "A pointer to the case reader from which this object is constructed",
155                           G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE );
156
157   object_class->set_property = psppire_case_file_set_property;
158   object_class->get_property = psppire_case_file_get_property;
159
160   g_object_class_install_property (object_class,
161                                    PROP_DATASHEET,
162                                    datasheet_spec);
163
164   g_object_class_install_property (object_class,
165                                    PROP_READER,
166                                    reader_spec);
167
168   signals [CASE_CHANGED] =
169     g_signal_new ("case-changed",
170                   G_TYPE_FROM_CLASS (class),
171                   G_SIGNAL_RUN_FIRST,
172                   0,
173                   NULL, NULL,
174                   g_cclosure_marshal_VOID__INT,
175                   G_TYPE_NONE,
176                   1,
177                   G_TYPE_INT);
178
179
180   signals [CASE_INSERTED] =
181     g_signal_new ("case-inserted",
182                   G_TYPE_FROM_CLASS (class),
183                   G_SIGNAL_RUN_FIRST,
184                   0,
185                   NULL, NULL,
186                   g_cclosure_marshal_VOID__INT,
187                   G_TYPE_NONE,
188                   1,
189                   G_TYPE_INT);
190
191
192   signals [CASES_DELETED] =
193     g_signal_new ("cases-deleted",
194                   G_TYPE_FROM_CLASS (class),
195                   G_SIGNAL_RUN_FIRST,
196                   0,
197                   NULL, NULL,
198                   gtkextra_VOID__INT_INT,
199                   G_TYPE_NONE,
200                   2,
201                   G_TYPE_INT, G_TYPE_INT);
202 }
203
204 static void
205 psppire_case_file_finalize (GObject *object)
206 {
207   PsppireCaseFile *cf = PSPPIRE_CASE_FILE (object);
208
209   if ( cf->accessible)
210     datasheet_destroy (cf->datasheet);
211
212   G_OBJECT_CLASS (parent_class)->finalize (object);
213 }
214
215 static void
216 psppire_case_file_init (PsppireCaseFile *cf)
217 {
218   cf->datasheet = NULL;
219   cf->accessible = FALSE;
220 }
221
222
223 /**
224  * psppire_case_file_new:
225  * @returns: a new #PsppireCaseFile object
226  *
227  * Creates a new #PsppireCaseFile.
228  */
229 PsppireCaseFile*
230 psppire_case_file_new (struct casereader *reader)
231 {
232   return g_object_new (G_TYPE_PSPPIRE_CASE_FILE,
233                        "casereader", reader,
234                        NULL);
235 }
236
237
238 gboolean
239 psppire_case_file_delete_cases (PsppireCaseFile *cf, casenumber n_cases, casenumber first)
240 {
241   g_return_val_if_fail (cf, FALSE);
242   g_return_val_if_fail (cf->datasheet, FALSE);
243   g_return_val_if_fail (cf->accessible, FALSE);
244
245   g_return_val_if_fail (first + n_cases <=
246                         psppire_case_file_get_case_count (cf), FALSE);
247
248   datasheet_delete_rows (cf->datasheet, first, n_cases);
249
250   g_signal_emit (cf, signals [CASES_DELETED], 0, first, n_cases);
251
252   return TRUE;
253 }
254
255 /* Insert case CC into the case file before POSN */
256 gboolean
257 psppire_case_file_insert_case (PsppireCaseFile *cf,
258                                struct ccase *cc,
259                                casenumber posn)
260 {
261   struct ccase tmp;
262   bool result ;
263
264   g_return_val_if_fail (cf, FALSE);
265   g_return_val_if_fail (cf->datasheet, FALSE);
266   g_return_val_if_fail (cf->accessible, FALSE);
267
268   case_clone (&tmp, cc);
269   result = datasheet_insert_rows (cf->datasheet, posn, &tmp, 1);
270
271   if ( result )
272     g_signal_emit (cf, signals [CASE_INSERTED], 0, posn);
273   else
274     g_warning ("Cannot insert case at position %ld\n", posn);
275
276   return result;
277 }
278
279
280 inline casenumber
281 psppire_case_file_get_case_count (const PsppireCaseFile *cf)
282 {
283   g_return_val_if_fail (cf, FALSE);
284   g_return_val_if_fail (cf->accessible, FALSE);
285
286   if ( ! cf->datasheet)
287     return 0;
288
289   return datasheet_get_row_cnt (cf->datasheet);
290 }
291
292 /* Copies the IDXth value from case CASENUM into VALUE.
293    If VALUE is null, then memory is allocated is allocated with
294    malloc.  Returns the value if successful, NULL on failure. */
295 union value *
296 psppire_case_file_get_value (const PsppireCaseFile *cf,
297                              casenumber casenum, size_t idx,
298                              union value *value, int width)
299 {
300   bool allocated;
301
302   g_return_val_if_fail (cf, false);
303   g_return_val_if_fail (cf->datasheet, false);
304
305   g_return_val_if_fail (idx < datasheet_get_column_cnt (cf->datasheet), false);
306
307   if (value == NULL)
308     {
309       value = xnmalloc (value_cnt_from_width (width), sizeof *value);
310       allocated = true;
311     }
312   else
313     allocated = false;
314   if (!datasheet_get_value (cf->datasheet, casenum, idx, value, width))
315     {
316       if (allocated)
317         free (value);
318       value = NULL;
319     }
320   return value;
321 }
322
323 void
324 psppire_case_file_clear (PsppireCaseFile *cf)
325 {
326   datasheet_destroy (cf->datasheet);
327   cf->datasheet = NULL;
328   g_signal_emit (cf, signals [CASES_DELETED], 0, 0, -1);
329 }
330
331 /* Set the IDXth value of case C to V.
332    Returns true if successful, false on I/O error. */
333 gboolean
334 psppire_case_file_set_value (PsppireCaseFile *cf, casenumber casenum, gint idx,
335                             union value *v, gint width)
336 {
337   bool ok;
338
339   g_return_val_if_fail (cf, FALSE);
340   g_return_val_if_fail (cf->datasheet, FALSE);
341
342   g_return_val_if_fail (idx < datasheet_get_column_cnt (cf->datasheet), FALSE);
343
344   ok = datasheet_put_value (cf->datasheet, casenum, idx, v, width);
345   if (ok)
346     g_signal_emit (cf, signals [CASE_CHANGED], 0, casenum);
347   return ok;
348 }
349
350
351
352 /* Set the IDXth value of case C using D_IN */
353 gboolean
354 psppire_case_file_data_in (PsppireCaseFile *cf, casenumber casenum, gint idx,
355                           struct substring input, const struct fmt_spec *fmt)
356 {
357   union value *value = NULL;
358   int width;
359   bool ok;
360
361   g_return_val_if_fail (cf, FALSE);
362   g_return_val_if_fail (cf->datasheet, FALSE);
363
364   g_return_val_if_fail (idx < datasheet_get_column_cnt (cf->datasheet), FALSE);
365
366   width = fmt_var_width (fmt);
367   value = xmalloca (value_cnt_from_width (width) * sizeof *value);
368   ok = (datasheet_get_value (cf->datasheet, casenum, idx, value, width)
369         && data_in (input, LEGACY_NATIVE, fmt->type, 0, 0, 0, value, width)
370         && datasheet_put_value (cf->datasheet, casenum, idx, value, width));
371
372   if (ok)
373     g_signal_emit (cf, signals [CASE_CHANGED], 0, casenum);
374
375   freea (value);
376
377   return TRUE;
378 }
379
380
381 void
382 psppire_case_file_sort (PsppireCaseFile *cf, struct case_ordering *ordering)
383 {
384   struct casereader *sorted_data;
385   gint c;
386
387   sorted_data = sort_execute (datasheet_make_reader (cf->datasheet), ordering);
388   cf->datasheet = datasheet_create (sorted_data);
389
390   /* FIXME: Need to have a signal to change a range of cases, instead of
391      calling a signal many times */
392   for ( c = 0 ; c < datasheet_get_row_cnt (cf->datasheet) ; ++c )
393     g_signal_emit (cf, signals [CASE_CHANGED], 0, c);
394 }
395
396
397 /* Resize the cases in the casefile, by inserting N_VALUES into every
398    one of them at the position immediately preceeding WHERE.
399 */
400 gboolean
401 psppire_case_file_insert_values (PsppireCaseFile *cf,
402                                  gint n_values, gint where)
403 {
404   g_return_val_if_fail (cf, FALSE);
405   g_return_val_if_fail (cf->accessible, FALSE);
406
407   if ( n_values == 0 )
408     return FALSE;
409
410   g_assert (n_values > 0);
411
412   if ( ! cf->datasheet )
413     cf->datasheet = datasheet_create (NULL);
414
415   {
416     union value *values = xcalloc (n_values, sizeof *values);
417     datasheet_insert_columns (cf->datasheet, values, n_values, where);
418     free (values);
419   }
420
421   return TRUE;
422 }
423
424
425 /* Fills C with the CASENUMth case.
426    Returns true on success, false otherwise.
427  */
428 gboolean
429 psppire_case_file_get_case (const PsppireCaseFile *cf, casenumber casenum,
430                            struct ccase *c)
431 {
432   g_return_val_if_fail (cf, FALSE);
433   g_return_val_if_fail (cf->datasheet, FALSE);
434
435   return datasheet_get_row (cf->datasheet, casenum, c);
436 }
437
438
439
440 struct casereader *
441 psppire_case_file_make_reader (PsppireCaseFile *cf)
442 {
443   struct casereader *r = datasheet_make_reader (cf->datasheet);
444   cf->accessible = FALSE;
445   return r;
446 }
447