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