1 /*
2 * gog-data-set.c : A Utility interface for managing GOData as attributes
3 *
4 * Copyright (C) 2003-2004 Jody Goldberg (jody@gnome.org)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) version 3.
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 St, Fifth Floor, Boston, MA 02110-1301
19 * USA
20 */
21
22 #include <goffice/goffice-config.h>
23 #include <goffice/goffice.h>
24
25 /**
26 * GogDatasetElement:
27 * @data: the #GOData
28 * @set: the owner data set.
29 * @dim_i: the dimension iside the dataset.
30 **/
31
32 /**
33 * GogDatasetClass:
34 * @base: base class
35 * @get_elem: gets i-th element.
36 * @set_dim: sets the data for i-th element.
37 * @dims: gest first and last valid elements indices.
38 * @dim_changed: called when an element has changed.
39 **/
40
41 GType
gog_dataset_get_type(void)42 gog_dataset_get_type (void)
43 {
44 static GType gog_dataset_type = 0;
45
46 if (!gog_dataset_type) {
47 static GTypeInfo const gog_dataset_info = {
48 sizeof (GogDatasetClass), /* class_size */
49 NULL, /* base_init */
50 NULL, /* base_finalize */
51 };
52
53 gog_dataset_type = g_type_register_static (G_TYPE_INTERFACE,
54 "GogDataset", &gog_dataset_info, 0);
55 }
56
57 return gog_dataset_type;
58 }
59
60 /**
61 * gog_dataset_dims:
62 * @set: #GogDataset
63 * @first: inclusive
64 * @last: _inclusive_
65 *
66 * FIXME ?? Fix what ??
67 * Stores the first and last valid indicises to get/set dim
68 * in @first and @last.
69 **/
70 void
gog_dataset_dims(GogDataset const * set,int * first,int * last)71 gog_dataset_dims (GogDataset const *set, int *first, int *last)
72 {
73 GogDatasetClass *klass;
74 g_return_if_fail (set);
75 klass = GOG_DATASET_GET_CLASS (set);
76 g_return_if_fail (klass != NULL);
77 g_return_if_fail (first != NULL);
78 g_return_if_fail (last != NULL);
79 (klass->dims) (set, first, last);
80 }
81
82 /**
83 * gog_dataset_get_dim:
84 * @set: #GogDataset
85 * @dim_i:
86 *
87 * Returns: (transfer none): the GOData associated with dimension @dim_i. Does NOT add a
88 * reference. or %NULL on failure.
89 **/
90 GOData *
gog_dataset_get_dim(GogDataset const * set,int dim_i)91 gog_dataset_get_dim (GogDataset const *set, int dim_i)
92 {
93 GogDatasetElement *elem;
94 g_return_val_if_fail (set, NULL);
95 elem = gog_dataset_get_elem (set, dim_i);
96 if (NULL == elem)
97 return NULL;
98 return elem->data;
99 }
100
101 /**
102 * gog_dataset_set_dim:
103 * @set: #GogDataset
104 * @dim_i: < 0 gets the name
105 * @val: (transfer full): #GOData
106 * @err: #GError
107 *
108 * Absorbs a ref to @val if it is non-%NULL
109 **/
110 void
gog_dataset_set_dim(GogDataset * set,int dim_i,GOData * val,GError ** err)111 gog_dataset_set_dim (GogDataset *set, int dim_i, GOData *val, GError **err)
112 {
113 GogDatasetClass *klass;
114 int first, last;
115
116 g_return_if_fail (set != NULL || val == NULL || GO_IS_DATA (val));
117
118 if (set == NULL || !GOG_IS_DATASET (set)) {
119 g_warning ("gog_dataset_set_dim called with invalid GogDataset");
120 goto done;
121 }
122
123 gog_dataset_dims (set, &first, &last);
124 if (dim_i < first || dim_i > last) {
125 g_warning ("gog_dataset_set_dim called with invalid index (%d)", dim_i);
126 goto done;
127 }
128 klass = GOG_DATASET_GET_CLASS (set);
129
130 /* short circuit */
131 if (val != gog_dataset_get_dim (set, dim_i)) {
132 gog_dataset_set_dim_internal (set, dim_i, val,
133 gog_object_get_graph (GOG_OBJECT (set)));
134
135 if (klass->set_dim)
136 (klass->set_dim) (set, dim_i, val, err);
137 if (klass->dim_changed)
138 (klass->dim_changed) (set, dim_i);
139 }
140
141 done :
142 /* absorb ref to orig, simplifies life cycle easier for new GODatas */
143 if (val != NULL)
144 g_object_unref (val);
145 }
146
147 /**
148 * gog_dataset_get_elem: (skip)
149 * @set: #GogDataset
150 * @dim_i:
151 *
152 * Returns: the GODataset associated with dimension @dim_i.
153 **/
154 GogDatasetElement *
gog_dataset_get_elem(GogDataset const * set,int dim_i)155 gog_dataset_get_elem (GogDataset const *set, int dim_i)
156 {
157 GogDatasetClass *klass = GOG_DATASET_GET_CLASS (set);
158 g_return_val_if_fail (klass != NULL, NULL);
159 return (klass->get_elem) (set, dim_i);
160 }
161
162 static void
cb_dataset_dim_changed(GOData * data,GogDatasetElement * elem)163 cb_dataset_dim_changed (GOData *data, GogDatasetElement *elem)
164 {
165 GogDatasetClass *klass = GOG_DATASET_GET_CLASS (elem->set);
166
167 g_return_if_fail (klass != NULL);
168 if (klass->dim_changed)
169 (klass->dim_changed) (elem->set, elem->dim_i);
170 }
171
172 /**
173 * gog_dataset_set_dim_internal:
174 * @set: #GogDataset
175 * @dim_i: the index
176 * @val: #GOData
177 * @graph: #GogGraph
178 *
179 * an internal routine to handle signal setup and teardown
180 **/
181 void
gog_dataset_set_dim_internal(GogDataset * set,int dim_i,GOData * val,GogGraph * graph)182 gog_dataset_set_dim_internal (GogDataset *set, int dim_i,
183 GOData *val, GogGraph *graph)
184 {
185 GogDatasetElement *elem = gog_dataset_get_elem (set, dim_i);
186
187 g_return_if_fail (elem != NULL);
188
189 if (graph != NULL) {
190 if (val == elem->data)
191 return;
192 if (val != NULL)
193 val = gog_graph_ref_data (graph, val);
194 if (elem->handler != 0) {
195 g_signal_handler_disconnect (G_OBJECT (elem->data),
196 elem->handler);
197 elem->handler = 0;
198 gog_graph_unref_data (graph, elem->data);
199 }
200 if (val != NULL)
201 elem->handler = g_signal_connect (
202 G_OBJECT (val), "changed",
203 G_CALLBACK (cb_dataset_dim_changed), elem);
204 } else {
205 if (val != NULL)
206 g_object_ref (val);
207 if (elem->data != NULL)
208 g_object_unref (elem->data);
209 }
210 elem->data = val;
211 elem->set = set;
212 elem->dim_i = dim_i;
213 gog_object_request_update (GOG_OBJECT (set));
214 }
215
216 void
gog_dataset_finalize(GogDataset * set)217 gog_dataset_finalize (GogDataset *set)
218 {
219 GogGraph *graph = gog_object_get_graph (GOG_OBJECT (set));
220 int first, last;
221
222 gog_dataset_dims (set, &first, &last);
223 while (first <= last)
224 gog_dataset_set_dim_internal (set, first++, NULL, graph);
225 }
226
227 void
gog_dataset_parent_changed(GogDataset * set,gboolean was_set)228 gog_dataset_parent_changed (GogDataset *set, gboolean was_set)
229 {
230 GogGraph *graph = gog_object_get_graph (GOG_OBJECT (set));
231 GogDatasetElement *elem;
232 GOData *dat;
233 int i, last;
234
235 for (gog_dataset_dims (set, &i, &last); i <= last ; i++) {
236 elem = gog_dataset_get_elem (set, i);
237 if (elem == NULL || elem->data == NULL)
238 continue;
239 dat = elem->data;
240 if (!was_set) {
241 g_object_ref (dat);
242 gog_dataset_set_dim_internal (set, i, NULL, graph);
243 elem->data = dat;
244 } else if (elem->handler == 0) {
245 elem->data = NULL; /* disable the short circuit */
246 gog_dataset_set_dim_internal (set, i, dat, graph);
247 g_object_unref (dat);
248 }
249 }
250 if (was_set)
251 gog_object_request_update (GOG_OBJECT (set));
252 }
253
254 void
gog_dataset_dup_to_simple(GogDataset const * src,GogDataset * dst)255 gog_dataset_dup_to_simple (GogDataset const *src, GogDataset *dst)
256 {
257 gint n, last;
258 GOData *src_dat, *dst_dat;
259
260 gog_dataset_dims (src, &n, &last);
261
262 for ( ; n <= last ; n++) {
263 unsigned int n_dimensions;
264
265 src_dat = gog_dataset_get_dim (src, n);
266 if (src_dat == NULL)
267 continue;
268 dst_dat = NULL;
269
270 n_dimensions = go_data_get_n_dimensions (src_dat);
271
272 /* for scalar and vector data, try to transform to values first
273 if we find non finite, use strings */
274
275 switch (n_dimensions) {
276 case 0:
277 {
278 char *str;
279 char *end;
280 double d;
281
282 str = go_data_get_scalar_string (src_dat);
283 d = g_strtod (str, &end);
284
285 dst_dat = (*end == 0) ?
286 go_data_scalar_val_new (d):
287 go_data_scalar_str_new (g_strdup (str), TRUE);
288
289 g_free (str);
290 }
291 break;
292 case 1:
293 {
294 double *d;
295 int i, n;
296 gboolean as_values = TRUE;
297
298 d = go_data_get_values (src_dat);
299 n = go_data_get_vector_size (src_dat);
300
301 for (i = 0; i < n; i++)
302 if (!go_finite (d[i])) {
303 as_values = FALSE;
304 break;
305 }
306
307 if (as_values)
308 /* we don't need to duplicate, since this is used only for
309 short lived objects */
310 dst_dat = go_data_vector_val_new (d, n, NULL);
311 else {
312 char **str = g_new (char*, n + 1);
313 str[n] = NULL;
314 for (i = 0; i < n; i++)
315 str[i] = go_data_get_vector_string (src_dat, i);
316
317 dst_dat = go_data_vector_str_new ((char const* const*) str,
318 n, g_free);
319 }
320 }
321 break;
322 case 2:
323 {
324 /* only values are supported so don't care */
325 GODataMatrixSize size;
326
327 go_data_get_matrix_size (src_dat, &size.rows, &size.columns);
328
329 dst_dat = go_data_matrix_val_new (go_data_get_values (src_dat),
330 size.rows, size.columns, NULL);
331 }
332 break;
333 default:
334 g_warning ("[GogDataSet::dup_to_simple] Source with invalid number of dimensions (%d)",
335 n_dimensions);
336 }
337 gog_dataset_set_dim (dst, n, dst_dat, NULL);
338 }
339 }
340