1 /*
2  * Copyright (C) 2006 Murray Cumming <murrayc@murrayc.com>
3  * Copyright (C) 2006 - 2011 Vivien Malerba <malerba@gnome-db.org>
4  * Copyright (C) 2010 David King <davidk@openismus.com>
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
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (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  02110-1301, USA.
19  */
20 #include "html.h"
21 #include <glib/gi18n-lib.h>
22 
23 static gint counter = 0;
24 
25 void
26 html_init_config (HtmlConfig *config)
27 {
28 	g_assert (config);
29 
30 	config->nodes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
31 }
32 
33 HtmlFile *
34 html_file_new (HtmlConfig *config, const gchar *name, const gchar *title)
35 {
36 	HtmlFile *file;
37 	xmlNodePtr topnode, head, node;
38 
39 	file = g_new0 (HtmlFile, 1);
40 	file->name = g_strdup (name);
41 	file->doc = xmlNewDoc (BAD_CAST "1.0");
42 	topnode = xmlNewDocNode (file->doc, NULL, BAD_CAST "html", NULL);
43 	xmlDocSetRootElement (file->doc, topnode);
44 
45 	/* head */
46 	head = xmlNewChild (topnode, NULL, BAD_CAST "head", NULL);
47 
48 	node = xmlNewChild (head, NULL, BAD_CAST "meta", NULL);
49 	xmlSetProp(node, BAD_CAST "content", BAD_CAST "charset=UTF-8");
50 	xmlSetProp(node, BAD_CAST "http-equiv", BAD_CAST "content-type");
51 
52 	node = xmlNewChild (head, NULL, BAD_CAST "title", BAD_CAST title);
53 	node = xmlNewChild (head, NULL, BAD_CAST "style",
54 BAD_CAST "body { "
55 "       margin: 0px; padding: 0px;  border:0px; "
56 "	font: 8pt/16pt georgia; "
57 "	color: #555753; "
58 "	margin: 5px; "
59 "	}"
60 ""
61 "a:visited, a:link { "
62 "        text-decoration: none ; color: #4085cd; "
63 "}"
64 ""
65 "a:hover { "
66 "        text-decoration: none; color: #FF0000;  "
67 "}"
68 ""
69 "p { "
70 "	font: 8pt/16pt georgia; "
71 "	margin-top: 0px; "
72 "	text-align: justify;"
73 "	}"
74 "h2 { "
75 "	font: italic normal 14pt georgia; "
76 "	letter-spacing: 1px; "
77 "	margin-bottom: 0px; "
78 "	color: #7D775C;"
79 "	}"
80 ""
81 "table {"
82 "	font-size: 8pt;"
83 "	/*border: 1pt solid #A8E775;*/"
84 "	padding: 10px;"
85 "}"
86 ""
87 "tr {"
88 "	background:  #EFEFEF;"
89 "}"
90 ""
91 "th {"
92 "	font-size: 12pt;"
93 "}"
94 ""
95 ".none {"
96 "        list-style : none;"
97 "	padding-left: 0px;"
98 "}"
99 ""
100 ".error {"
101 "        color: #FF0000;"
102 "        font: bold;"
103 "        /*font-size: large;*/"
104 "}"
105 ""
106 ".warning {"
107 "        color: #ff9900;"
108 "        font: bold;"
109 "        /*font-size: medium;*/"
110 "}"
111 ""
112 ".notice {"
113 "        color: #22bb00;"
114 "        font: bold;"
115 "        /*font-size: medium;*/"
116 "}"
117 ""
118 "#inactive {"
119 "        background-color: #CCCCFF;"
120 "}"
121 ""
122 ".null {"
123 "       color: lightblue;"
124 "}"
125 );
126 
127 	/* body */
128 	node = xmlNewChild (topnode, NULL, BAD_CAST "body", NULL);
129 	file->body = node;
130 
131 	/* title */
132 	node = xmlNewChild (file->body, NULL, BAD_CAST "h1", BAD_CAST title);
133 	xmlSetProp(node, BAD_CAST "class", BAD_CAST "title");
134 
135 #ifdef NO
136 	/* toc */
137 	file->toc = xmlNewChild (file->body, NULL, "ul", _("Table of contents"));
138 	xmlSetProp(file->toc, "class", (xmlChar*)"none");
139 #endif
140 
141 	/* add to @config's list of files */
142 	config->all_files = g_slist_append (config->all_files, file);
143 
144 	return file;
145 }
146 
147 gboolean
148 html_file_write (HtmlFile *file, HtmlConfig *config)
149 {
150 	gchar *str;
151 
152 	str = g_strdup_printf ("%s/%s", config->dir, file->name);
153 	gint i;
154 	i = xmlSaveFormatFile (str, file->doc, TRUE);
155 	if (i == -1)
156 		g_warning (_("Error writing XML file %s"), str);
157 	g_free (str);
158 
159 	return i!=-1;
160 }
161 
162 void
163 html_file_free  (HtmlFile *file)
164 {
165 	xmlFreeDoc (file->doc);
166 	g_free (file->name);
167 	g_free (file);
168 }
169 
170 void
171 html_declare_node (HtmlConfig *config, const gchar *path, xmlNodePtr node)
172 {
173 	html_declare_node_own (config, g_strdup (path), node);
174 }
175 
176 void
177 html_declare_node_own (HtmlConfig *config, gchar *path, xmlNodePtr node)
178 {
179 	xmlNodePtr enode;
180 
181 	enode = g_hash_table_lookup (config->nodes, path);
182 	if (enode) {
183 		g_warning ("Node path %s is already attributed", path);
184 		g_free (path);
185 		return;
186 	}
187 
188 	g_hash_table_insert (config->nodes, path, node);
189 	/*g_print ("--- Added node @ %s\n", path);*/
190 }
191 
192 /*
193  * if @link_to starts with a '/' then a # is prepended (internal link)
194  */
195 void
196 real_html_add_link_to_node (xmlNodePtr node, const gchar *text, const gchar *link_to)
197 {
198 	xmlNodePtr href;
199 	gchar *tmp;
200 
201 	href = xmlNewNode (NULL, (xmlChar*)"a");
202 	tmp = g_strdup_printf (" [%s] ", text);
203 	xmlNodeSetContent (href, BAD_CAST tmp);
204 	g_free (tmp);
205 	if (node->children) {
206 		xmlNodePtr sibl;
207 
208 		sibl = node->children;
209 		while (sibl && xmlNodeIsText (sibl))
210 			sibl = sibl->next;
211 		if (sibl)
212 			xmlAddPrevSibling (sibl, href);
213 		else
214 			xmlAddChild (node, href);
215 	}
216 	else
217 		xmlAddChild (node, href);
218 	if (*link_to == '/') {
219 		tmp = g_strdup_printf ("#%s", link_to);
220 		xmlSetProp(href, BAD_CAST "href", BAD_CAST tmp);
221 		g_free (tmp);
222 	}
223 	else
224 		xmlSetProp(href, BAD_CAST "href", BAD_CAST link_to);
225 }
226 
227 void
228 html_add_link_to_node (HtmlConfig *config, const gchar *nodepath, const gchar *text, const gchar *link_to)
229 {
230 	xmlNodePtr node;
231 
232 	node = g_hash_table_lookup (config->nodes, nodepath);
233 	if (!node) {
234 		g_warning ("Can't link non existant node '%s'", nodepath);
235 		return;
236 	}
237 
238 	real_html_add_link_to_node (node, text, link_to);
239 }
240 
241 void
242 html_add_to_toc (G_GNUC_UNUSED HtmlConfig *config, HtmlFile *file, const gchar *text, const gchar *link_to)
243 {
244 	xmlNodePtr li;
245 
246 	li = xmlNewChild (file->toc, NULL, BAD_CAST "li", NULL);
247 	real_html_add_link_to_node (li, text, link_to);
248 }
249 
250 xmlNodePtr
251 html_add_header (HtmlConfig *config, HtmlFile *file, const gchar *text)
252 {
253 	xmlNodePtr hnode, ntmp;
254 	gchar *tmp;
255 
256 	hnode = xmlNewChild (file->body, NULL, BAD_CAST "h2", BAD_CAST text);
257 	tmp = g_strdup_printf ("/a/%d", counter++);
258 	html_add_to_toc (config, file, text, tmp);
259 	ntmp = xmlNewChild (hnode, NULL, BAD_CAST "a", BAD_CAST "");
260 	xmlSetProp(ntmp, BAD_CAST "name", BAD_CAST tmp);
261 	g_free (tmp);
262 
263 	return hnode;
264 }
265 
266 void
267 html_mark_path_error (HtmlConfig *config, const gchar *nodepath)
268 {
269 	xmlNodePtr node;
270 
271 	node = g_hash_table_lookup (config->nodes, nodepath);
272 	if (!node) {
273 		g_warning ("Can't link non existant node '%s'", nodepath);
274 		return;
275 	}
276 	html_mark_node_error (config, node);
277 }
278 
279 void
280 html_mark_node_error (G_GNUC_UNUSED HtmlConfig *config, xmlNodePtr node)
281 {
282 	xmlSetProp(node, BAD_CAST "class", BAD_CAST "error");
283 }
284 
285 void
286 html_mark_node_warning (G_GNUC_UNUSED HtmlConfig *config, xmlNodePtr node)
287 {
288 	xmlSetProp(node, BAD_CAST "class", BAD_CAST "warning");
289 }
290 
291 void
292 html_mark_node_notice (G_GNUC_UNUSED HtmlConfig *config, xmlNodePtr node)
293 {
294 	xmlSetProp(node, BAD_CAST "class", BAD_CAST "notice");
295 }
296 
297 xmlNodePtr
298 html_render_attribute_str (xmlNodePtr parent, const gchar *node_type,
299 		      const gchar *att_name, const gchar *att_val)
300 {
301 	xmlNodePtr node;
302 	gchar *tmp;
303 
304 	tmp = g_strdup_printf ("%s = %s", att_name, att_val);
305 	node = xmlNewChild (parent, NULL, BAD_CAST node_type, BAD_CAST tmp);
306 	g_free (tmp);
307 
308 	return node;
309 }
310 
311 xmlNodePtr
312 html_render_attribute_bool (xmlNodePtr parent, const gchar *node_type,
313 			    const gchar *att_name, gboolean att_val)
314 {
315 	xmlNodePtr node;
316 	gchar *tmp;
317 
318 	tmp = g_strdup_printf ("%s = %s", att_name, att_val ? _("Yes") : _("No"));
319 	node = xmlNewChild (parent, NULL, BAD_CAST node_type, BAD_CAST tmp);
320 	g_free (tmp);
321 
322 	return node;
323 }
324 
325 xmlNodePtr
326 html_render_data_model (xmlNodePtr parent, GdaDataModel *model)
327 {
328 	xmlNodePtr node, tr, td;
329         gint rows, cols, i;
330 
331         g_return_val_if_fail (GDA_IS_DATA_MODEL (model), NULL);
332 
333         node = xmlNewChild (parent, NULL, BAD_CAST "table", BAD_CAST "");
334 
335 	cols = gda_data_model_get_n_columns (model);
336 	rows = gda_data_model_get_n_rows (model);
337 
338         /* set the table structure */
339 	tr = xmlNewChild (node, NULL, BAD_CAST "tr", NULL);
340         for (i = 0; i < cols; i++) {
341                 GdaColumn *column;
342 
343                 column = gda_data_model_describe_column (model, i);
344                 if (!column) {
345                         xmlFreeNode (node);
346                         return NULL;
347                 }
348 
349                 td = xmlNewChild (tr, NULL, BAD_CAST "th", BAD_CAST gda_column_get_name (column));
350         }
351 
352 	/* add the model data to the XML output */
353         if (rows > 0) {
354                 gint r, c;
355 
356                 for (r = 0; r < rows; r++) {
357 			tr = xmlNewChild (node, NULL, BAD_CAST "tr", BAD_CAST "");
358                         for (c = 0 ; c < cols; c++) {
359                                 GValue *value;
360 
361                                 value = (GValue *) gda_data_model_get_value_at (model, c, r, NULL);
362 				if (!value) {
363 					xmlNodePtr p;
364 					td = xmlNewChild (tr, NULL, BAD_CAST "td", NULL);
365 					p = xmlNewChild (td, NULL, BAD_CAST "p", BAD_CAST "ERROR");
366 					xmlSetProp(p, BAD_CAST "class", BAD_CAST "null");
367 				}
368 				else if (gda_value_is_null (value)) {
369 					xmlNodePtr p;
370 					td = xmlNewChild (tr, NULL, BAD_CAST "td", NULL);
371 					p = xmlNewChild (td, NULL, BAD_CAST "p", BAD_CAST "NULL");
372 					xmlSetProp(p, BAD_CAST "class", BAD_CAST "null");
373 				}
374 				else {
375 					gchar *str;
376 					if (G_VALUE_TYPE (value) == G_TYPE_BOOLEAN)
377 						str = g_strdup (g_value_get_boolean (value) ? "TRUE" : "FALSE");
378 					else
379 						str = gda_value_stringify (value);
380 					td = xmlNewChild (tr, NULL, BAD_CAST "td", BAD_CAST str);
381 					g_free (str);
382 				}
383                         }
384                 }
385         }
386 
387         return node;
388 }
389