No issues found
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) version 3.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with the program; if not, see <http://www.gnu.org/licenses/>
14 *
15 *
16 * Authors:
17 * Chris Lahey <clahey@ximian.com>
18 *
19 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
20 *
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include <glib/gstdio.h>
31 #include <libxml/parser.h>
32 #include <libxml/xmlmemory.h>
33
34 #include <libedataserver/libedataserver.h>
35
36 #include "e-util/e-util.h"
37 #include "libevolution-utils/e-xml-utils.h"
38
39 #include "e-table-specification.h"
40
41 /* workaround for avoiding API breakage */
42 #define etsp_get_type e_table_specification_get_type
43 G_DEFINE_TYPE (ETableSpecification, etsp, G_TYPE_OBJECT)
44
45 static void
46 etsp_finalize (GObject *object)
47 {
48 ETableSpecification *etsp = E_TABLE_SPECIFICATION (object);
49 gint i;
50
51 if (etsp->columns) {
52 for (i = 0; etsp->columns[i]; i++) {
53 g_object_unref (etsp->columns[i]);
54 }
55 g_free (etsp->columns);
56 etsp->columns = NULL;
57 }
58
59 if (etsp->state)
60 g_object_unref (etsp->state);
61 etsp->state = NULL;
62
63 g_free (etsp->click_to_add_message);
64 etsp->click_to_add_message = NULL;
65
66 g_free (etsp->domain);
67 etsp->domain = NULL;
68
69 G_OBJECT_CLASS (etsp_parent_class)->finalize (object);
70 }
71
72 static void
73 etsp_class_init (ETableSpecificationClass *class)
74 {
75 GObjectClass *object_class = G_OBJECT_CLASS (class);
76
77 object_class->finalize = etsp_finalize;
78 }
79
80 static void
81 etsp_init (ETableSpecification *etsp)
82 {
83 etsp->columns = NULL;
84 etsp->state = NULL;
85
86 etsp->alternating_row_colors = TRUE;
87 etsp->no_headers = FALSE;
88 etsp->click_to_add = FALSE;
89 etsp->click_to_add_end = FALSE;
90 etsp->horizontal_draw_grid = FALSE;
91 etsp->vertical_draw_grid = FALSE;
92 etsp->draw_focus = TRUE;
93 etsp->horizontal_scrolling = FALSE;
94 etsp->horizontal_resize = FALSE;
95 etsp->allow_grouping = TRUE;
96
97 etsp->cursor_mode = E_CURSOR_SIMPLE;
98 etsp->selection_mode = GTK_SELECTION_MULTIPLE;
99
100 etsp->click_to_add_message = NULL;
101 etsp->domain = NULL;
102 }
103
104 /**
105 * e_table_specification_new:
106 *
107 * Creates a new %ETableSpecification object. This object is used to hold the
108 * information about the rendering information for ETable.
109 *
110 * Returns: a newly created %ETableSpecification object.
111 */
112 ETableSpecification *
113 e_table_specification_new (void)
114 {
115 ETableSpecification *etsp = g_object_new (E_TYPE_TABLE_SPECIFICATION, NULL);
116
117 return (ETableSpecification *) etsp;
118 }
119
120 /**
121 * e_table_specification_load_from_file:
122 * @specification: An ETableSpecification that you want to modify
123 * @filename: a filename that contains an ETableSpecification
124 *
125 * This routine modifies @specification to reflect the state described
126 * by the file @filename.
127 *
128 * Returns: TRUE on success, FALSE on failure.
129 */
130 gboolean
131 e_table_specification_load_from_file (ETableSpecification *specification,
132 const gchar *filename)
133 {
134 xmlDoc *doc;
135
136 doc = e_xml_parse_file (filename);
137 if (doc) {
138 xmlNode *node = xmlDocGetRootElement (doc);
139 e_table_specification_load_from_node (specification, node);
140 xmlFreeDoc (doc);
141 return TRUE;
142 }
143 return FALSE;
144 }
145
146 /**
147 * e_table_specification_load_from_string:
148 * @specification: An ETableSpecification that you want to modify
149 * @xml: a stringified representation of an ETableSpecification description.
150 *
151 * This routine modifies @specification to reflect the state described
152 * by @xml. @xml is typically returned by e_table_specification_save_to_string
153 * or it can be embedded in your source code.
154 *
155 * Returns: TRUE on success, FALSE on failure.
156 */
157 gboolean
158 e_table_specification_load_from_string (ETableSpecification *specification,
159 const gchar *xml)
160 {
161 xmlDoc *doc;
162 doc = xmlParseMemory ((gchar *) xml, strlen (xml));
163 if (doc) {
164 xmlNode *node = xmlDocGetRootElement (doc);
165 e_table_specification_load_from_node (specification, node);
166 xmlFreeDoc (doc);
167 return TRUE;
168 }
169
170 return FALSE;
171 }
172
173 /**
174 * e_table_specification_load_from_node:
175 * @specification: An ETableSpecification that you want to modify
176 * @node: an xmlNode with an XML ETableSpecification description.
177 *
178 * This routine modifies @specification to reflect the state described
179 * by @node.
180 */
181 void
182 e_table_specification_load_from_node (ETableSpecification *specification,
183 const xmlNode *node)
184 {
185 gchar *temp;
186 xmlNode *children;
187 GList *list = NULL, *list2;
188 gint i;
189
190 specification->no_headers = e_xml_get_bool_prop_by_name (node, (const guchar *)"no-headers");
191 specification->click_to_add = e_xml_get_bool_prop_by_name (node, (const guchar *)"click-to-add");
192 specification->click_to_add_end = e_xml_get_bool_prop_by_name (node, (const guchar *)"click-to-add-end") && specification->click_to_add;
193 specification->alternating_row_colors = e_xml_get_bool_prop_by_name_with_default (node, (const guchar *)"alternating-row-colors", TRUE);
194 specification->horizontal_draw_grid = e_xml_get_bool_prop_by_name (node, (const guchar *)"horizontal-draw-grid");
195 specification->vertical_draw_grid = e_xml_get_bool_prop_by_name (node, (const guchar *)"vertical-draw-grid");
196 if (e_xml_get_bool_prop_by_name_with_default (node, (const guchar *)"draw-grid", TRUE) ==
197 e_xml_get_bool_prop_by_name_with_default (node, (const guchar *)"draw-grid", FALSE)) {
198 specification->horizontal_draw_grid =
199 specification->vertical_draw_grid = e_xml_get_bool_prop_by_name (node, (const guchar *)"draw-grid");
200 }
201 specification->draw_focus = e_xml_get_bool_prop_by_name_with_default (node, (const guchar *)"draw-focus", TRUE);
202 specification->horizontal_scrolling = e_xml_get_bool_prop_by_name_with_default (node, (const guchar *)"horizontal-scrolling", FALSE);
203 specification->horizontal_resize = e_xml_get_bool_prop_by_name_with_default (node, (const guchar *)"horizontal-resize", FALSE);
204 specification->allow_grouping = e_xml_get_bool_prop_by_name_with_default (node, (const guchar *)"allow-grouping", TRUE);
205
206 specification->selection_mode = GTK_SELECTION_MULTIPLE;
207 temp = e_xml_get_string_prop_by_name (node, (const guchar *)"selection-mode");
208 if (temp && !g_ascii_strcasecmp (temp, "single")) {
209 specification->selection_mode = GTK_SELECTION_SINGLE;
210 } else if (temp && !g_ascii_strcasecmp (temp, "browse")) {
211 specification->selection_mode = GTK_SELECTION_BROWSE;
212 } else if (temp && !g_ascii_strcasecmp (temp, "extended")) {
213 specification->selection_mode = GTK_SELECTION_MULTIPLE;
214 }
215 g_free (temp);
216
217 specification->cursor_mode = E_CURSOR_SIMPLE;
218 temp = e_xml_get_string_prop_by_name (node, (const guchar *)"cursor-mode");
219 if (temp && !g_ascii_strcasecmp (temp, "line")) {
220 specification->cursor_mode = E_CURSOR_LINE;
221 } else if (temp && !g_ascii_strcasecmp (temp, "spreadsheet")) {
222 specification->cursor_mode = E_CURSOR_SPREADSHEET;
223 }
224 g_free (temp);
225
226 g_free (specification->click_to_add_message);
227 specification->click_to_add_message =
228 e_xml_get_string_prop_by_name (
229 node, (const guchar *)"_click-to-add-message");
230
231 g_free (specification->domain);
232 specification->domain =
233 e_xml_get_string_prop_by_name (
234 node, (const guchar *)"gettext-domain");
235 if (specification->domain && !*specification->domain) {
236 g_free (specification->domain);
237 specification->domain = NULL;
238 }
239
240 if (specification->state)
241 g_object_unref (specification->state);
242 specification->state = NULL;
243 if (specification->columns) {
244 for (i = 0; specification->columns[i]; i++) {
245 g_object_unref (specification->columns[i]);
246 }
247 g_free (specification->columns);
248 }
249 specification->columns = NULL;
250
251 for (children = node->xmlChildrenNode; children; children = children->next) {
252 if (!strcmp ((gchar *) children->name, "ETableColumn")) {
253 ETableColumnSpecification *col_spec = e_table_column_specification_new ();
254
255 e_table_column_specification_load_from_node (col_spec, children);
256 list = g_list_append (list, col_spec);
257 } else if (specification->state == NULL && !strcmp ((gchar *) children->name, "ETableState")) {
258 specification->state = e_table_state_new ();
259 e_table_state_load_from_node (specification->state, children);
260 e_table_sort_info_set_can_group (specification->state->sort_info, specification->allow_grouping);
261 }
262 }
263
264 if (specification->state == NULL) {
265 /* Make the default state. */
266 specification->state = e_table_state_vanilla (g_list_length (list));
267 }
268
269 specification->columns = g_new (ETableColumnSpecification *, g_list_length (list) + 1);
270 for (list2 = list, i = 0; list2; list2 = g_list_next (list2), i++) {
271 specification->columns[i] = list2->data;
272 }
273 specification->columns[i] = NULL;
274 g_list_free (list);
275 }
276
277 /**
278 * e_table_specification_save_to_file:
279 * @specification: An %ETableSpecification that you want to save
280 * @filename: a file name to store the specification.
281 *
282 * This routine stores the @specification into @filename.
283 *
284 * Returns: 0 on success or -1 on error.
285 */
286 gint
287 e_table_specification_save_to_file (ETableSpecification *specification,
288 const gchar *filename)
289 {
290 xmlDoc *doc;
291 gint ret;
292
293 g_return_val_if_fail (specification != NULL, -1);
294 g_return_val_if_fail (filename != NULL, -1);
295 g_return_val_if_fail (E_IS_TABLE_SPECIFICATION (specification), -1);
296
297 if ((doc = xmlNewDoc ((const guchar *)"1.0")) == NULL)
298 return -1;
299
300 xmlDocSetRootElement (doc, e_table_specification_save_to_node (specification, doc));
301
302 ret = e_xml_save_file (filename, doc);
303
304 xmlFreeDoc (doc);
305
306 return ret;
307 }
308
309 /**
310 * e_table_specification_save_to_string:
311 * @specification: An %ETableSpecification that you want to stringify
312 *
313 * Saves the state of @specification to a string.
314 *
315 * Returns: an g_alloc() allocated string containing the stringified
316 * representation of @specification. This stringified representation
317 * uses XML as a convenience.
318 */
319 gchar *
320 e_table_specification_save_to_string (ETableSpecification *specification)
321 {
322 gchar *ret_val;
323 xmlChar *string;
324 gint length;
325 xmlDoc *doc;
326
327 g_return_val_if_fail (specification != NULL, NULL);
328 g_return_val_if_fail (E_IS_TABLE_SPECIFICATION (specification), NULL);
329
330 doc = xmlNewDoc ((const guchar *)"1.0");
331 xmlDocSetRootElement (doc, e_table_specification_save_to_node (specification, doc));
332 xmlDocDumpMemory (doc, &string, &length);
333
334 ret_val = g_strdup ((gchar *) string);
335 xmlFree (string);
336 return ret_val;
337 }
338
339 /**
340 * e_table_specification_save_to_node:
341 * @specification: An ETableSpecification that you want to store.
342 * @doc: Node where the specification is saved
343 *
344 * This routine saves the %ETableSpecification state in the object @specification
345 * into the xmlDoc represented by @doc.
346 *
347 * Returns: The node that has been attached to @doc with the contents
348 * of the ETableSpecification.
349 */
350 xmlNode *
351 e_table_specification_save_to_node (ETableSpecification *specification,
352 xmlDoc *doc)
353 {
354 xmlNode *node;
355 const gchar *s;
356
357 g_return_val_if_fail (doc != NULL, NULL);
358 g_return_val_if_fail (specification != NULL, NULL);
359 g_return_val_if_fail (E_IS_TABLE_SPECIFICATION (specification), NULL);
360
361 node = xmlNewNode (NULL, (const guchar *)"ETableSpecification");
362 e_xml_set_bool_prop_by_name (node, (const guchar *)"no-headers", specification->no_headers);
363 e_xml_set_bool_prop_by_name (node, (const guchar *)"click-to-add", specification->click_to_add);
364 e_xml_set_bool_prop_by_name (node, (const guchar *)"click-to-add-end", specification->click_to_add_end && specification->click_to_add);
365 e_xml_set_bool_prop_by_name (node, (const guchar *)"alternating-row-colors", specification->alternating_row_colors);
366 e_xml_set_bool_prop_by_name (node, (const guchar *)"horizontal-draw-grid", specification->horizontal_draw_grid);
367 e_xml_set_bool_prop_by_name (node, (const guchar *)"vertical-draw-grid", specification->vertical_draw_grid);
368 e_xml_set_bool_prop_by_name (node, (const guchar *)"draw-focus", specification->draw_focus);
369 e_xml_set_bool_prop_by_name (node, (const guchar *)"horizontal-scrolling", specification->horizontal_scrolling);
370 e_xml_set_bool_prop_by_name (node, (const guchar *)"horizontal-resize", specification->horizontal_resize);
371 e_xml_set_bool_prop_by_name (node, (const guchar *)"allow-grouping", specification->allow_grouping);
372
373 switch (specification->selection_mode) {
374 case GTK_SELECTION_SINGLE:
375 s = "single";
376 break;
377 case GTK_SELECTION_BROWSE:
378 s = "browse";
379 break;
380 default:
381 case GTK_SELECTION_MULTIPLE:
382 s = "extended";
383 }
384 xmlSetProp (node, (const guchar *)"selection-mode", (guchar *) s);
385 if (specification->cursor_mode == E_CURSOR_LINE)
386 s = "line";
387 else
388 s = "cell";
389 xmlSetProp (node, (const guchar *)"cursor-mode", (guchar *) s);
390
391 xmlSetProp (node, (const guchar *)"_click-to-add-message", (guchar *) specification->click_to_add_message);
392 xmlSetProp (node, (const guchar *)"gettext-domain", (guchar *) specification->domain);
393
394 if (specification->columns) {
395 gint i;
396
397 for (i = 0; specification->columns[i]; i++)
398 e_table_column_specification_save_to_node (
399 specification->columns[i],
400 node);
401 }
402
403 if (specification->state)
404 e_table_state_save_to_node (specification->state, node);
405
406 return node;
407 }
408
409 /**
410 * e_table_specification_duplicate:
411 * @spec: specification to duplicate
412 *
413 * This creates a copy of the %ETableSpecification @spec
414 *
415 * Returns: The duplicated %ETableSpecification.
416 */
417 ETableSpecification *
418 e_table_specification_duplicate (ETableSpecification *spec)
419 {
420 ETableSpecification *new_spec;
421 gchar *spec_str;
422
423 g_return_val_if_fail (spec != NULL, NULL);
424 g_return_val_if_fail (E_IS_TABLE_SPECIFICATION (spec), NULL);
425
426 new_spec = e_table_specification_new ();
427 spec_str = e_table_specification_save_to_string (spec);
428 if (!e_table_specification_load_from_string (new_spec, spec_str)) {
429 g_warning ("Unable to duplicate ETable specification");
430 g_object_unref (new_spec);
431 new_spec = NULL;
432 }
433 g_free (spec_str);
434
435 return new_spec;
436 }