Use PtkUIXml and glade file to build a better config dialog.
[lxde/lxpanel.git] / src / ptk-ui-xml / ptk-xml-tree.c
1 /*
2 * ptk-xml-tree.h - Over-simplified mini xml dom implementation
3 *
4 * Copyright 2008 PCMan <pcman.tw@gmail.com>
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,
19 * MA 02110-1301, USA.
20 */
21
22 #include <string.h>
23 #include "ptk-xml-tree.h"
24 #include "glib-mem.h"
25
26 typedef struct _ParseInfo ParseInfo;
27
28 struct _ParseInfo
29 {
30 XmlNode* tree;
31 GArray* parse_stack;
32 };
33
34 #define xml_node_new() g_slice_new0(XmlNode);
35
36 static void start_element( GMarkupParseContext *context,
37 const gchar *element_name,
38 const gchar **attribute_names,
39 const gchar **attribute_values,
40 gpointer user_data,
41 GError **error )
42 {
43 ParseInfo* info = (ParseInfo*)user_data;
44 XmlNode *cur_node, *parent;
45 if( info->parse_stack->len > 0 ) {
46 parent = g_array_index( info->parse_stack,
47 XmlNode*,
48 info->parse_stack->len - 1 );
49 } else {
50 parent = NULL;
51 }
52 cur_node = xml_node_new();
53 cur_node->name = g_strdup( element_name );
54 cur_node->props = g_strdupv( (gchar**)attribute_names );
55 cur_node->vals = g_strdupv( (gchar**)attribute_values );
56 if( parent )
57 {
58 cur_node->parent = parent;
59 parent->children = g_slist_prepend( parent->children, cur_node );
60 }
61 /* push current node to the stack */
62 g_array_append_val( info->parse_stack, cur_node );
63 }
64
65 static void end_element( GMarkupParseContext *context,
66 const gchar *element_name,
67 gpointer user_data,
68 GError **error )
69 {
70 ParseInfo* info = (ParseInfo*)user_data;
71 int i = info->parse_stack->len - 1;
72 XmlNode* cur_node = g_array_index( info->parse_stack,
73 XmlNode*, i );
74
75 if( cur_node->cdata )
76 {
77 GString* str = (GString*)cur_node->cdata;
78 cur_node->cdata = g_strdup( str->str );
79 g_string_free( str, TRUE );
80 }
81
82 if( cur_node->children )
83 cur_node->children = g_slist_reverse( cur_node->children );
84
85 /* begin tag is different from end tag, error! */
86 if( strcmp( cur_node->name, element_name ) )
87 {
88 g_markup_parse_context_end_parse( context, NULL );
89 return;
90 }
91
92 /* pop current node from the stack */
93 g_array_remove_index( info->parse_stack, i );
94 }
95
96 static void cdata( GMarkupParseContext *context,
97 const gchar *text,
98 gsize text_len,
99 gpointer user_data,
100 GError **error )
101 {
102 ParseInfo* info = (ParseInfo*)user_data;
103 GString* str;
104 XmlNode* cur_node = g_array_index( info->parse_stack,
105 XmlNode*,
106 info->parse_stack->len - 1 );
107 if( !cur_node->cdata )
108 {
109 str = g_string_sized_new(256);
110 cur_node->cdata = (char*)str;
111 }
112 else
113 str = (GString*)cur_node->cdata;
114 g_string_append_len( str, text, text_len );
115 }
116
117 static void parse_error( GMarkupParseContext *context,
118 GError *error, gpointer user_data )
119 {
120 ParseInfo* info = (ParseInfo*)user_data;
121 XmlNode* cur_node = g_array_index( info->parse_stack,
122 XmlNode*,
123 info->parse_stack->len - 1 );
124 if( cur_node->cdata )
125 {
126 GString* str = (GString*)cur_node->cdata;
127 g_string_free( str, TRUE );
128 cur_node->cdata = NULL;
129 }
130 g_markup_parse_context_end_parse( context, NULL );
131 }
132
133 static GMarkupParser parser =
134 {
135 start_element,
136 end_element,
137 cdata,
138 NULL,
139 parse_error
140 };
141
142 XmlNode* xml_tree_load( const char* filename )
143 {
144 gsize data_len;
145 char* data;
146
147 if ( g_file_get_contents( filename, &data, &data_len, NULL ) )
148 {
149 GMarkupParseContext * ctx;
150 ParseInfo info;
151 info.tree = xml_node_new();
152 info.parse_stack = g_array_sized_new(FALSE, TRUE, sizeof(XmlNode*), 32);
153 g_array_append_val( info.parse_stack, info.tree );
154 ctx = g_markup_parse_context_new( &parser,
155 (GMarkupParseFlags)0,
156 &info, NULL );
157 if( !g_markup_parse_context_parse( ctx, data, data_len, NULL ) )
158 {
159 xml_tree_free( info.tree );
160 info.tree = NULL;
161 }
162 g_markup_parse_context_free( ctx );
163 g_array_free( info.parse_stack, TRUE );
164 return info.tree;
165 }
166 return NULL;
167 }
168
169 void xml_node_free( XmlNode* node )
170 {
171 GSList* l;
172 XmlNode* child;
173
174 if( node->children )
175 {
176 for( l = node->children; l; l = l->next )
177 {
178 child = (XmlNode*)l->data;
179 /* break the linkage between parent and child,
180 or subsequent recursive call to xml_node_free on
181 the child node will try to remove it from the parent again. */
182 child->parent = NULL;
183 xml_node_free( child );
184 }
185 g_slist_free( node->children );
186 }
187
188 /* delete the child node from its parent, ifneeded */
189 if( G_LIKELY( node->parent && node->parent->children) )
190 node->parent->children = g_slist_remove( node->parent->children, node );
191
192 g_free( node->name );
193 g_free( node->cdata );
194 g_strfreev( node->props );
195 g_strfreev( node->vals );
196 g_slice_free( XmlNode, node );
197 }
198
199 const char* xml_node_get_prop( XmlNode* node, const char* name )
200 {
201 char **prop, **val;
202 for( prop = node->props, val = node->vals; *prop; ++prop, ++val )
203 {
204 if( *prop && 0 == strcmp(name, *prop) )
205 return *val;
206 }
207 return NULL;
208 }