Added the ability to sort the working file.
[pspp-builds.git] / src / ui / gui / psppire-case-file.c
1 /* 
2     PSPPIRE --- A Graphical User Interface for PSPP
3     Copyright (C) 2006  Free Software Foundation
4     Written by John Darrington
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19     02110-1301, USA. */
20
21
22 #include <string.h>
23 #include <stdlib.h>
24
25 #include "psppire-object.h"
26 #include "psppire-case-file.h"
27
28 #include <gtksheet/gtkextra-marshal.h>
29
30 #include <data/case.h>
31 #include <data/casefile.h>
32 #include <data/data-in.h>
33
34 #include <math/sort.h>
35
36 /* --- prototypes --- */
37 static void psppire_case_file_class_init        (PsppireCaseFileClass   *class);
38 static void psppire_case_file_init      (PsppireCaseFile        *case_file);
39 static void psppire_case_file_finalize  (GObject                *object);
40
41
42 /* --- variables --- */
43 static GObjectClass     *parent_class = NULL;
44
45 enum  {CASE_CHANGED, 
46        CASE_INSERTED,
47        CASES_DELETED,
48        n_SIGNALS};
49
50 static guint signal[n_SIGNALS];
51
52
53 /* --- functions --- */
54 /**
55  * psppire_case_file_get_type:
56  * @returns: the type ID for accelerator groups.
57  */
58 GType
59 psppire_case_file_get_type (void)
60 {
61   static GType object_type = 0;
62
63   if (!object_type)
64     {
65       static const GTypeInfo object_info = {
66         sizeof (PsppireCaseFileClass),
67         (GBaseInitFunc) NULL,
68         (GBaseFinalizeFunc) NULL,
69         (GClassInitFunc) psppire_case_file_class_init,
70         NULL,   /* class_finalize */
71         NULL,   /* class_data */
72         sizeof (PsppireCaseFile),
73         0,      /* n_preallocs */
74         (GInstanceInitFunc) psppire_case_file_init,
75       };
76
77       object_type = g_type_register_static (G_TYPE_PSPPIRE_OBJECT, "PsppireCaseFile",
78                                             &object_info, 0);
79     }
80
81   return object_type;
82 }
83
84
85 static void
86 psppire_case_file_class_init (PsppireCaseFileClass *class)
87 {
88   GObjectClass *object_class = G_OBJECT_CLASS (class);
89
90   parent_class = g_type_class_peek_parent (class);
91
92   object_class->finalize = psppire_case_file_finalize;
93
94   signal[CASE_CHANGED] =
95     g_signal_new ("case_changed",
96                   G_TYPE_FROM_CLASS(class),
97                   G_SIGNAL_RUN_FIRST,
98                   0,
99                   NULL, NULL,
100                   g_cclosure_marshal_VOID__INT,
101                   G_TYPE_NONE, 
102                   1,
103                   G_TYPE_INT);
104
105
106   signal[CASE_INSERTED] =
107     g_signal_new ("case_inserted",
108                   G_TYPE_FROM_CLASS(class),
109                   G_SIGNAL_RUN_FIRST,
110                   0,
111                   NULL, NULL,
112                   g_cclosure_marshal_VOID__INT,
113                   G_TYPE_NONE, 
114                   1,
115                   G_TYPE_INT);
116
117
118   signal[CASES_DELETED] =
119     g_signal_new ("cases_deleted",
120                   G_TYPE_FROM_CLASS(class),
121                   G_SIGNAL_RUN_FIRST,
122                   0,
123                   NULL, NULL,
124                   gtkextra_VOID__INT_INT,
125                   G_TYPE_NONE, 
126                   2,
127                   G_TYPE_INT, G_TYPE_INT);
128 }
129
130 static void
131 psppire_case_file_finalize (GObject *object)
132 {
133   PsppireCaseFile *cf = PSPPIRE_CASE_FILE (object);
134   
135   if ( cf->casefile) 
136     casefile_destroy(cf->casefile);
137
138   G_OBJECT_CLASS (parent_class)->finalize (object);
139 }
140
141 static void
142 psppire_case_file_init (PsppireCaseFile *cf)
143 {
144   cf->casefile = 0;
145 }
146
147 /**
148  * psppire_case_file_new:
149  * @returns: a new #PsppireCaseFile object
150  * 
151  * Creates a new #PsppireCaseFile. 
152  */
153 PsppireCaseFile*
154 psppire_case_file_new (gint var_cnt)
155 {
156   PsppireCaseFile *cf = g_object_new (G_TYPE_PSPPIRE_CASE_FILE, NULL);
157
158   cf->casefile = casefile_create(var_cnt);
159
160   return cf;
161 }
162
163
164
165 /* Append a case to the case file */
166 gboolean
167 psppire_case_file_append_case(PsppireCaseFile *cf, 
168                               struct ccase *c)
169 {
170   bool result ;
171   gint posn ;
172
173   g_return_val_if_fail(cf, FALSE);
174   g_return_val_if_fail(cf->casefile, FALSE);
175
176   posn = casefile_get_case_cnt(cf->casefile);
177
178   result = casefile_append(cf->casefile, c);
179   
180   g_signal_emit(cf, signal[CASE_INSERTED], 0, posn);
181                 
182   return result;
183 }
184
185
186 inline gint
187 psppire_case_file_get_case_count(const PsppireCaseFile *cf)
188 {
189   g_return_val_if_fail(cf, FALSE);
190   
191   if ( ! cf->casefile) 
192     return 0;
193
194   return casefile_get_case_cnt(cf->casefile);
195 }
196
197 /* Return the IDXth value from case CASENUM.
198    The return value must not be freed or written to
199  */
200 const union value *
201 psppire_case_file_get_value(const PsppireCaseFile *cf, gint casenum, gint idx)
202 {
203   const union value *v; 
204   struct ccase c;
205   struct casereader *reader =  casefile_get_random_reader (cf->casefile);
206
207   casereader_seek(reader, casenum);
208
209   casereader_read(reader, &c);
210
211   v = case_data(&c, idx);
212   casereader_destroy(reader);
213   case_destroy(&c);
214
215   return v;
216 }
217
218 void
219 psppire_case_file_clear(PsppireCaseFile *cf)
220 {
221   casefile_destroy(cf->casefile);
222   cf->casefile = 0;
223   g_signal_emit(cf, signal[CASES_DELETED], 0, 0, -1);
224 }
225
226 /* Set the IDXth value of case C using FF and DATA */
227 gboolean
228 psppire_case_file_set_value(PsppireCaseFile *cf, gint casenum, gint idx,
229                             struct data_in *d_in)
230 {
231   struct ccase cc ;
232
233   struct casereader *reader =  casefile_get_random_reader (cf->casefile);
234
235   casereader_seek(reader, casenum);
236   casereader_read(reader, &cc);
237
238   /* Cast away const in flagrant abuse of the casefile */
239   d_in->v = (union value *) case_data(&cc, idx);
240
241   if ( ! data_in(d_in) ) 
242     g_warning("Cant set value\n");
243
244   case_destroy(&cc);
245   casereader_destroy(reader);
246
247   g_signal_emit(cf, signal[CASE_CHANGED], 0, casenum);
248
249   return TRUE;
250 }
251
252
253 void
254 psppire_case_file_sort(PsppireCaseFile *cf, const struct sort_criteria *sc)
255 {
256   gint c;
257   struct casereader *reader = casefile_get_reader(cf->casefile);
258   cf->casefile = sort_execute(reader, sc);
259
260   /* FIXME: Need to have a signal to change a range of cases, instead of
261      calling a signal many times */
262   for ( c = 0 ; c < casefile_get_case_cnt(cf->casefile) ; ++c ) 
263     g_signal_emit(cf, signal[CASE_CHANGED], 0, c);
264 }