1 /*
2 * bulia byak <buliabyak@users.sf.net>
3 */
5 #define SP_REPR_CSS_C
8 #include <glibmm/ustring.h>
10 #include "xml/repr.h"
11 #include "xml/simple-node.h"
13 using Inkscape::Util::List;
14 using Inkscape::XML::AttributeRecord;
15 using Inkscape::XML::SimpleNode;
16 using Inkscape::XML::Node;
17 using Inkscape::XML::NodeType;
19 struct SPCSSAttrImpl : public SimpleNode, public SPCSSAttr {
20 public:
21 SPCSSAttrImpl() : SimpleNode(g_quark_from_static_string("css")) {}
23 NodeType type() const { return Inkscape::XML::ELEMENT_NODE; }
25 protected:
26 SimpleNode *_duplicate() const { return new SPCSSAttrImpl(*this); }
27 };
29 static void sp_repr_css_add_components(SPCSSAttr *css, Node *repr, gchar const *attr);
31 SPCSSAttr *
32 sp_repr_css_attr_new()
33 {
34 return new SPCSSAttrImpl();
35 }
37 void
38 sp_repr_css_attr_unref(SPCSSAttr *css)
39 {
40 g_assert(css != NULL);
41 Inkscape::GC::release((Node *) css);
42 }
44 SPCSSAttr *sp_repr_css_attr(Node *repr, gchar const *attr)
45 {
46 g_assert(repr != NULL);
47 g_assert(attr != NULL);
49 SPCSSAttr *css = sp_repr_css_attr_new();
50 sp_repr_css_add_components(css, repr, attr);
51 return css;
52 }
54 SPCSSAttr *sp_repr_css_attr_inherited(Node *repr, gchar const *attr)
55 {
56 g_assert(repr != NULL);
57 g_assert(attr != NULL);
59 SPCSSAttr *css = sp_repr_css_attr_new();
61 sp_repr_css_add_components(css, repr, attr);
62 Node *current = sp_repr_parent(repr);
64 while (current) {
65 sp_repr_css_add_components(css, current, attr);
66 current = sp_repr_parent(current);
67 }
69 return css;
70 }
72 static void
73 sp_repr_css_add_components(SPCSSAttr *css, Node *repr, gchar const *attr)
74 {
75 g_assert(css != NULL);
76 g_assert(repr != NULL);
77 g_assert(attr != NULL);
79 char const *data = repr->attribute(attr);
80 sp_repr_css_attr_add_from_string(css, data);
81 }
83 char const *
84 sp_repr_css_property(SPCSSAttr *css, gchar const *name, gchar const *defval)
85 {
86 g_assert(css != NULL);
87 g_assert(name != NULL);
89 char const *attr = ((Node *)css)->attribute(name);
90 return ( attr == NULL
91 ? defval
92 : attr );
93 }
95 bool
96 sp_repr_css_property_is_unset(SPCSSAttr *css, gchar const *name)
97 {
98 g_assert(css != NULL);
99 g_assert(name != NULL);
101 char const *attr = ((Node *)css)->attribute(name);
102 return (attr && !strcmp(attr, "inkscape:unset"));
103 }
106 void
107 sp_repr_css_set_property(SPCSSAttr *css, gchar const *name, gchar const *value)
108 {
109 g_assert(css != NULL);
110 g_assert(name != NULL);
112 sp_repr_set_attr((Node *) css, name, value);
113 }
115 void
116 sp_repr_css_unset_property(SPCSSAttr *css, gchar const *name)
117 {
118 g_assert(css != NULL);
119 g_assert(name != NULL);
121 sp_repr_set_attr((Node *) css, name, "inkscape:unset");
122 }
124 double
125 sp_repr_css_double_property(SPCSSAttr *css, gchar const *name, double defval)
126 {
127 g_assert(css != NULL);
128 g_assert(name != NULL);
130 return sp_repr_get_double_attribute((Node *) css, name, defval);
131 }
133 gchar *
134 sp_repr_css_write_string(SPCSSAttr *css)
135 {
136 Glib::ustring buffer;
138 for ( List<AttributeRecord const> iter = css->attributeList() ;
139 iter ; ++iter )
140 {
141 if (iter->value && !strcmp(iter->value, "inkscape:unset")) {
142 continue;
143 }
145 buffer.append(g_quark_to_string(iter->key));
146 buffer.push_back(':');
147 buffer.append(iter->value);
148 if (rest(iter)) {
149 buffer.push_back(';');
150 }
151 }
153 return (buffer.empty() ? NULL : g_strdup (buffer.c_str()));
154 }
156 void
157 sp_repr_css_set(Node *repr, SPCSSAttr *css, gchar const *attr)
158 {
159 g_assert(repr != NULL);
160 g_assert(css != NULL);
161 g_assert(attr != NULL);
163 gchar *value = sp_repr_css_write_string(css);
165 repr->setAttribute(attr, value);
167 if (value) g_free (value);
168 }
170 void
171 sp_repr_css_print(SPCSSAttr *css)
172 {
173 for ( List<AttributeRecord const> iter = css->attributeList() ;
174 iter ; ++iter )
175 {
176 g_print(g_quark_to_string(iter->key));
177 g_print(":\t");
178 g_print(iter->value);
179 g_print("\n");
180 }
181 }
183 void
184 sp_repr_css_merge(SPCSSAttr *dst, SPCSSAttr *src)
185 {
186 g_assert(dst != NULL);
187 g_assert(src != NULL);
189 dst->mergeFrom(src, "");
190 }
192 void
193 sp_repr_css_attr_add_from_string(SPCSSAttr *css, gchar const *data)
194 {
195 if (data != NULL) {
196 char *new_str = g_strdup(data);
197 char **token = g_strsplit(new_str, ";", 0);
198 for (char **ctoken = token; *ctoken != NULL; ctoken++) {
199 char *current = g_strstrip(*ctoken);
200 char *key = current;
201 char *val;
202 for (val = key; *val != '\0'; val++)
203 if (*val == ':') break;
204 if (*val == '\0') break;
205 *val++ = '\0';
206 key = g_strstrip(key);
207 val = g_strstrip(val);
208 if (*val == '\0') break;
210 /* fixme: CSS specifies that later declarations override earlier ones with the same
211 key. (Ref: http://www.w3.org/TR/REC-CSS2/cascade.html#cascading-order point 4.)
212 Either add a precondition that there are no key duplicates in the string, or get rid
213 of the below condition (documenting the change that data[] will override existing
214 values in *css), or process the list in reverse order. */
215 if (!css->attribute(key))
216 sp_repr_set_attr((Node *) css, key, val);
217 }
218 g_strfreev(token);
219 g_free(new_str);
220 }
221 }
223 void
224 sp_repr_css_change(Node *repr, SPCSSAttr *css, gchar const *attr)
225 {
226 g_assert(repr != NULL);
227 g_assert(css != NULL);
228 g_assert(attr != NULL);
230 SPCSSAttr *current = sp_repr_css_attr(repr, attr);
231 sp_repr_css_merge(current, css);
232 sp_repr_css_set(repr, current, attr);
234 sp_repr_css_attr_unref(current);
235 }
237 void
238 sp_repr_css_change_recursive(Node *repr, SPCSSAttr *css, gchar const *attr)
239 {
240 g_assert(repr != NULL);
241 g_assert(css != NULL);
242 g_assert(attr != NULL);
244 sp_repr_css_change(repr, css, attr);
246 for (Node *child = repr->firstChild(); child != NULL; child = child->next()) {
247 sp_repr_css_change_recursive(child, css, attr);
248 }
249 }
252 /*
253 Local Variables:
254 mode:c++
255 c-file-style:"stroustrup"
256 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
257 indent-tabs-mode:nil
258 fill-column:99
259 End:
260 */
261 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :