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