1 /****************************************************************************
2 Copyright (C) 1987-2015 by Jeffery P. Hansen
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
18 Last edit by hansen on Sun Feb 22 18:12:11 2009
19 ****************************************************************************/
20 /*
21
22 This module implements a subset of html for use in tkgate comments.
23
24 The implemented elements are:
25 <value-of name="name"> Special variable value
26 <a href="url"></a> Hyperlink
27 <a name="tag"> Hyperlink tag
28 <b></b> Bold
29 <i></i> Italic
30 <h3></h3> Heading 3
31 <h2></h2> Heading 2
32 <h1></h1> Heading 1
33 <small></small> Small font
34 <big></big> Big font
35 <tt></tt> Fixed-width font
36 <pre></pre> Preformated
37 <br> Break
38 <p> Paragraph
39 <font size=n color=color face=face></font> Set font characteristics
40 <hr> Horizontal rule
41 &??; Special character
42
43 Special pre-processed tags:
44 <tutorial-navigation> Generates html for navigation bar in tutorials
45
46 */
47 #include <stdio.h>
48 #include <ctype.h>
49 #include <sys/time.h>
50 #include <stdarg.h>
51
52 #include "tkgate.h"
53 #include "print.h"
54
55 //
56 // Testing only
57 //
58 //#include <X11/Xatom.h>
59
60 /*
61 * Step size for extending the array of html tag options.
62 */
63 #define HTML_OSTEP 8
64
65 void expandSpecialDirs(char *file);
66
67 typedef void HtmlHandlerFunc(Html *h,HtmlTag *tag);
68
69 /*
70 * Private class for specifying functions to handle html tags
71 */
72 typedef struct {
73 char *tag; /* Tag */
74 HtmlHandlerFunc *func; /* Handling function for tag */
75 } HtmlHandler;
76
77 typedef struct {
78 char *spec; /* Special character specifier */
79 char *text; /* Raw text for replacement */
80 char *pretag; /* Html code to preceed text */
81 char *posttag; /* Html code to follow text */
82 } HtmlSpecialSpec;
83
84 void Html_handle_basic(Html *h, HtmlTag *tag);
85 void Html_handle_valueOf(Html *h, HtmlTag *tag);
86 void Html_handle_a(Html *h, HtmlTag *tag);
87 void Html_handle_heading(Html *h, HtmlTag *tag);
88 void Html_handle_img(Html *h, HtmlTag *tag);
89
90 /*
91 * Table of html tags that are recognized by tkgate
92 */
93 HtmlHandler htmlHandlers[] = {
94 {"a", Html_handle_a },
95 {"b", Html_handle_basic },
96 {"tt", Html_handle_basic },
97 {"i", Html_handle_basic },
98 {"big", Html_handle_basic },
99 {"small", Html_handle_basic },
100 {"font", Html_handle_basic },
101 {"h1", Html_handle_heading },
102 {"h2", Html_handle_heading },
103 {"h3", Html_handle_heading },
104 {"value-of", Html_handle_valueOf },
105 {"img", Html_handle_img },
106 {0, 0}
107 };
108
109 /*
110 * Table of html special characters that are recognized by tkgate
111 */
112 HtmlSpecialSpec htmlSpecialSpecs[] = {
113 {" ", " ", 0, 0},
114 {">", ">", 0, 0},
115 {"<", "<", 0, 0},
116 {"&", "&", 0, 0},
117
118
119 {"Α", "A", "<font face=symbol>", "</font>"},
120 {"Β", "B", "<font face=symbol>", "</font>"},
121 {"Γ", "G", "<font face=symbol>", "</font>"},
122 {"Δ", "D", "<font face=symbol>", "</font>"},
123 {"Ε", "E", "<font face=symbol>", "</font>"},
124 {"Ζ", "Z", "<font face=symbol>", "</font>"},
125 {"Η", "H", "<font face=symbol>", "</font>"},
126 {"Θ", "Q", "<font face=symbol>", "</font>"},
127 {"Ι", "I", "<font face=symbol>", "</font>"},
128 {"Κ", "K", "<font face=symbol>", "</font>"},
129 {"Λ", "L", "<font face=symbol>", "</font>"},
130 {"Μ", "M", "<font face=symbol>", "</font>"},
131 {"Ν", "N", "<font face=symbol>", "</font>"},
132 {"Ξ", "X", "<font face=symbol>", "</font>"},
133 {"Ο", "O", "<font face=symbol>", "</font>"},
134 {"Π", "P", "<font face=symbol>", "</font>"},
135 {"Ρ", "R", "<font face=symbol>", "</font>"},
136 {"Σ", "S", "<font face=symbol>", "</font>"},
137 {"Τ", "T", "<font face=symbol>", "</font>"},
138 {"Υ", "U", "<font face=symbol>", "</font>"},
139 {"Φ", "F", "<font face=symbol>", "</font>"},
140 {"Χ", "C", "<font face=symbol>", "</font>"},
141 {"Ψ", "Y", "<font face=symbol>", "</font>"},
142 {"Ω", "W", "<font face=symbol>", "</font>"},
143 {"α", "a", "<font face=symbol>", "</font>"},
144 {"β", "b", "<font face=symbol>", "</font>"},
145 {"γ", "g", "<font face=symbol>", "</font>"},
146 {"δ", "d", "<font face=symbol>", "</font>"},
147 {"ε", "e", "<font face=symbol>", "</font>"},
148 {"ζ", "z", "<font face=symbol>", "</font>"},
149 {"η", "h", "<font face=symbol>", "</font>"},
150 {"θ", "q", "<font face=symbol>", "</font>"},
151 {"ι", "i", "<font face=symbol>", "</font>"},
152 {"κ", "k", "<font face=symbol>", "</font>"},
153 {"λ", "l", "<font face=symbol>", "</font>"},
154 {"μ", "m", "<font face=symbol>", "</font>"},
155 {"ν", "n", "<font face=symbol>", "</font>"},
156 {"ξ", "x", "<font face=symbol>", "</font>"},
157 {"ο", "o", "<font face=symbol>", "</font>"},
158 {"π", "p", "<font face=symbol>", "</font>"},
159 {"ρ", "r", "<font face=symbol>", "</font>"},
160 {"ς", "V", "<font face=symbol>", "</font>"},
161 {"σ", "s", "<font face=symbol>", "</font>"},
162 {"τ", "t", "<font face=symbol>", "</font>"},
163 {"υ", "u", "<font face=symbol>", "</font>"},
164 {"φ", "f", "<font face=symbol>", "</font>"},
165 {"χ", "c", "<font face=symbol>", "</font>"},
166 {"ψ", "y", "<font face=symbol>", "</font>"},
167 {"ω", "w", "<font face=symbol>", "</font>"},
168 {"ϑ","J", "<font face=symbol>", "</font>"},
169 {"ϒ", "\241", "<font face=symbol>", "</font>"},
170 {"ϖ", "v", "<font face=symbol>", "</font>"},
171
172 {"•", "\267", "<font face=symbol>", "</font>"},
173 {"…", "\274", "<font face=symbol>", "</font>"},
174 {"′", "\242", "<font face=symbol>", "</font>"},
175 {"″", "\262", "<font face=symbol>", "</font>"},
176 {"‾", "\140", "<font face=symbol>", "</font>"},
177 {"⁄", "\244", "<font face=symbol>", "</font>"},
178 {"℘", "\303", "<font face=symbol>", "</font>"},
179 {"ℑ", "\301", "<font face=symbol>", "</font>"},
180 {"ℜ", "\302", "<font face=symbol>", "</font>"},
181 {"™", "\324", "<font face=symbol>", "</font>"},
182 {"ℵ", "\300", "<font face=symbol>", "</font>"},
183 {"←", "\254", "<font face=symbol>", "</font>"},
184 {"↑", "\255", "<font face=symbol>", "</font>"},
185 {"→", "\256", "<font face=symbol>", "</font>"},
186 {"↓", "\257", "<font face=symbol>", "</font>"},
187 {"↔", "\253", "<font face=symbol>", "</font>"},
188 {"↵", "\277", "<font face=symbol>", "</font>"},
189 {"⇐", "\334", "<font face=symbol>", "</font>"},
190 {"⇑", "\335", "<font face=symbol>", "</font>"},
191 {"⇒", "\336", "<font face=symbol>", "</font>"},
192 {"⇓", "\337", "<font face=symbol>", "</font>"},
193 {"⇔", "\333", "<font face=symbol>", "</font>"},
194 {"∀", "\042", "<font face=symbol>", "</font>"},
195 {"∂", "\266", "<font face=symbol>", "</font>"},
196 {"∃", "\044", "<font face=symbol>", "</font>"},
197 {"∅", "\306", "<font face=symbol>", "</font>"},
198 {"∇", "\321", "<font face=symbol>", "</font>"},
199 {"∈", "\316", "<font face=symbol>", "</font>"},
200 {"∉", "\317", "<font face=symbol>", "</font>"},
201 {"∋", "\267", "<font face=symbol>", "</font>"}, /* NOT CORRECT */
202 {"∏", "\325", "<font face=symbol>", "</font>"},
203 {"∑", "\345", "<font face=symbol>", "</font>"},
204 {"−", "-", "<font face=symbol>", "</font>"},
205 {"∗", "*", "<font face=symbol>", "</font>"},
206 {"√", "\326", "<font face=symbol>", "</font>"},
207 {"∝", "\265", "<font face=symbol>", "</font>"},
208 {"∞", "\245", "<font face=symbol>", "</font>"},
209 {"∠", "\320", "<font face=symbol>", "</font>"},
210 {"∧", "\331", "<font face=symbol>", "</font>"},
211 {"∨", "\332", "<font face=symbol>", "</font>"},
212 {"∩", "\307", "<font face=symbol>", "</font>"},
213 {"∪", "\310", "<font face=symbol>", "</font>"},
214 {"∫", "\362", "<font face=symbol>", "</font>"},
215 {"∴", "\134", "<font face=symbol>", "</font>"},
216 {"∼", "\176", "<font face=symbol>", "</font>"},
217 {"≅", "\100", "<font face=symbol>", "</font>"},
218 {"≈", "\273", "<font face=symbol>", "</font>"},
219 {"≠", "\271", "<font face=symbol>", "</font>"},
220 {"≡", "\272", "<font face=symbol>", "</font>"},
221 {"≤", "\243", "<font face=symbol>", "</font>"},
222 {"≥", "\263", "<font face=symbol>", "</font>"},
223 {"⊂", "\314", "<font face=symbol>", "</font>"},
224 {"⊃", "\311", "<font face=symbol>", "</font>"},
225 {"⊄", "\313", "<font face=symbol>", "</font>"},
226 {"⊆", "\315", "<font face=symbol>", "</font>"},
227 {"⊇", "\312", "<font face=symbol>", "</font>"},
228 {"⊕", "\305", "<font face=symbol>", "</font>"},
229 {"⊗", "\304", "<font face=symbol>", "</font>"},
230 {"⊥", "\267", "<font face=symbol>", "</font>"}, /* NOT CORRECT */
231 {"⋅", "\327", "<font face=symbol>", "</font>"},
232 {"⌈", "\351", "<font face=symbol>", "</font>"},
233 {"⌉", "\371", "<font face=symbol>", "</font>"},
234 {"⌊", "\353", "<font face=symbol>", "</font>"},
235 {"⌋", "\373", "<font face=symbol>", "</font>"},
236 {"⟨", "\341", "<font face=symbol>", "</font>"},
237 {"⟩", "\361", "<font face=symbol>", "</font>"},
238 {"◊", "\340", "<font face=symbol>", "</font>"},
239 {"♠", "\252", "<font face=symbol>", "</font>"},
240 {"♣", "\247", "<font face=symbol>", "</font>"},
241 {"♥", "\251", "<font face=symbol>", "</font>"},
242 {"♦", "\250", "<font face=symbol>", "</font>"},
243
244 {0,0,0,0}
245 };
246
247 #define DATA_STEP_SIZE 512
248
249 static HtmlContext default_context = {
250 .hc_font = { .gateFont = {FF_HELVETICA, FP_ROMAN, FS_NORMAL} },
251 .hc_pixelColor.xColor.pixel = 0,
252 .hc_link = 0,
253 .hc_tag = 0,
254 .hc_preformat = 1,
255 .hc_next = 0
256 };
257
Html_getEncoder(Html * h)258 static Encoder *Html_getEncoder(Html *h)
259 {
260 if (h->h_target == TD_X11)
261 return Circuit_getDisplayEncoder(TkGate.circuit);
262 else
263 return Circuit_getPSEncoder(TkGate.circuit);
264 }
265
HtmlFont_isEqual(const HtmlFont * a,const HtmlFont * b)266 int HtmlFont_isEqual(const HtmlFont *a,const HtmlFont *b)
267 {
268 if (memcmp(a, b, sizeof(HtmlFont)) == 0)
269 return (1);
270 else
271 return (0);
272 }
273
HtmlFont_updatePoints(HtmlFont * font)274 void HtmlFont_updatePoints(HtmlFont *font)
275 {
276 if (font->gateFont.family == FF_KANJI)
277 font->points = getKanjiFontSize(font->gateFont.size);
278 else
279 font->points = getFontSize(font->gateFont.size);
280 }
281
HtmlFont_init(HtmlFont * font,GateFont gateFont,fontsize_t points)282 HtmlFont *HtmlFont_init(HtmlFont *font,GateFont gateFont,fontsize_t points)
283 {
284 int i;
285
286 ob_touch(font);
287
288 memcpy(&font->gateFont, &gateFont, sizeof (GateFont));
289 font->points = points;
290
291 if (gateFont.family != FF_KANJI) {
292 for (i = 0;i < FS_MAX;i++)
293 if (getFontSize(i) >= points) break;
294 } else {
295 for (i = 0;i < FS_MAX;i++)
296 if (getKanjiFontSize(i) >= points) break;
297 }
298 if (i >= FS_MAX) i = FS_MAX - 1;
299 font->gateFont.size = i;
300
301 return font;
302 }
303
new_HtmlTag()304 HtmlTag *new_HtmlTag()
305 {
306 HtmlTag *ht = OM_MALLOC(HtmlTag);
307
308 memset(ht, 0, sizeof (HtmlTag));
309
310 return (ht);
311 }
312
delete_HtmlTag(HtmlTag * ht)313 void delete_HtmlTag(HtmlTag *ht)
314 {
315 if (ht->ht_name) ob_free(ht->ht_name);
316 if (ht->ht_options) {
317 int i;
318
319 for (i = 0;i < ht->ht_numOptions;i++) {
320 ob_free(ht->ht_options[i].hto_label);
321 ob_free(ht->ht_options[i].hto_value);
322 }
323
324 ob_free(ht->ht_options);
325 }
326 ob_free(ht);
327 }
328
329 /*****************************************************************************
330 * Break down an options string into tags and values. Return the
331 * number of tags parsed, or -1 if there was an error.
332 *****************************************************************************/
Html_parseTag(const char * tag)333 HtmlTag *Html_parseTag(const char *tag)
334 {
335 char name[STRMAX];
336 char options[STRMAX];
337 char ptag[STRMAX],pvalue[STRMAX];
338 char *p;
339 char c;
340 HtmlTag *ht = 0;
341
342 if (sscanf(tag,"< %[^ \t\n>] %[^>] %c",name,options,&c) == 3 && c == '>') {
343 /* Tag with options */
344 } else if (sscanf(tag,"< %[^ \t\n>] %c",name,&c) == 2 && c == '>') {
345 /* Tag with no options */
346 *options = 0;
347 } else {
348 /* Ignore badly formatted tag */
349 return 0;
350 }
351
352 ht = new_HtmlTag();
353 ht->ht_name = ob_strdup(name);
354
355 p = options;
356 while (*p && isspace(*p)) p++;
357 while (*p) {
358
359 if (sscanf(p,"%[^=] = \"%[^\"]",ptag,pvalue) == 2) {
360 /* ok - advance past option */
361 while (*p && *p != '=') p++;
362 while (*p && *p != '"') p++;
363 if (*p) p++;
364 while (*p && *p != '"') p++;
365 if (*p) p++;
366 } else if (sscanf(p,"%[^=] = %s",ptag,pvalue) == 2) {
367 /* ok - advance past option */
368 while (*p && *p != '=') p++;
369 if (*p) p++;
370 while (*p && isspace(*p)) p++;
371 while (*p && !isspace(*p)) p++;
372 } else {
373 while (*p && isspace(*p)) p++; /* Ignore unparsable tag */
374 while (*p && !isspace(*p)) p++;
375 continue;
376 }
377
378 if ((ht->ht_numOptions % HTML_OSTEP) == 0) {
379 if (ht->ht_numOptions == 0)
380 ht->ht_options = (HtmlTagOpt*) ob_malloc(sizeof(HtmlTagOpt)*HTML_OSTEP,"HtmlTagOpt");
381 else
382 ht->ht_options = (HtmlTagOpt*) ob_realloc(ht->ht_options,sizeof(HtmlTagOpt)*(ht->ht_numOptions + HTML_OSTEP));
383 }
384
385 ht->ht_options[ht->ht_numOptions].hto_label = ob_strdup(ptag);
386 ht->ht_options[ht->ht_numOptions].hto_value = ob_strdup(pvalue);
387 ht->ht_numOptions++;
388
389 while (*p && isspace(*p)) p++;
390 }
391
392 return ht;
393 }
394
HtmlContext_stringWidth(HtmlContext * hc,const char * text,int len)395 static int HtmlContext_stringWidth(HtmlContext *hc,const char *text,int len)
396 {
397 switch (hc->hc_html->h_target) {
398 case TD_X11 :
399 if (hc->hc_is16bit)
400 return XTextWidth16(hc->hc_xFont,(XChar2b*)text,len/2);
401 else
402 return GatePainterContext_textWidth(TkGate.commentContext,
403 hc->hc_font.gateFont, text, len);
404 case TD_PRINT :
405 return PSStringWidth(&hc->hc_font,text,len);
406 }
407 return 0;
408 }
409
HtmlFont_print(HtmlFont * hf,FILE * f)410 void HtmlFont_print(HtmlFont *hf,FILE *f)
411 {
412 char name[STRMAX];
413 getFontName(name,hf->gateFont.family,hf->gateFont.prop,hf->gateFont.size,1);
414 fprintf(f,"%s",name);
415 }
416
HtmlContext_activateFont(HtmlContext * hc)417 static void HtmlContext_activateFont(HtmlContext *hc)
418 {
419 ob_touch(hc);
420
421 switch (hc->hc_html->h_target) {
422 case TD_X11 : {
423 #ifdef DEBUG
424 printf("%p: activate-x ",hc);HtmlFont_print(&hc->hc_font,stdout);printf("\n");
425 #endif
426
427 hc->hc_xFont = GetXFont(hc->hc_font.gateFont, TkGate.circuit->zoom_factor);
428 hc->hc_is16bit = (hc->hc_font.gateFont.family == FF_KANJI);
429
430 if (hc->hc_is16bit)
431 hc->hc_spaceWidth = XTextWidth16(hc->hc_xFont, (XChar2b*)" ", 1);
432 else
433 hc->hc_spaceWidth = GatePainterContext_textWidth(
434 TkGate.commentContext, hc->hc_font.gateFont, " ", 1);
435
436 hc->hc_fontMetrics = GatePainterContext_fontMetrics(
437 TkGate.commentContext, &hc->hc_font.gateFont);
438 }
439 break;
440 case TD_PRINT :
441 #ifdef DEBUG
442 printf("activate-ps ");HtmlFont_print(&hc->hc_font,stdout);printf("\n");
443 #endif
444 hc->hc_xFont = 0;
445 hc->hc_is16bit = (hc->hc_font.gateFont.family == FF_KANJI);
446
447 if (hc->hc_is16bit)
448 hc->hc_spaceWidth = PSStringWidth(&hc->hc_font," ",2);
449 else
450 hc->hc_spaceWidth = PSStringWidth(&hc->hc_font," ",1);
451
452 hc->hc_fontMetrics.ascent = (int)(hc->hc_font.points*0.2);
453 hc->hc_fontMetrics.descent = (int)(hc->hc_font.points*1.2) -
454 hc->hc_fontMetrics.descent;
455 break;
456 }
457 }
458
new_HtmlContext(HtmlContext * base,Html * html)459 static HtmlContext *new_HtmlContext(HtmlContext *base,Html *html)
460 {
461 HtmlContext *hc = OM_MALLOC(HtmlContext);
462
463 if (base)
464 *hc = *base;
465 else {
466 *hc = default_context;
467 hc->hc_pixelColor = TkGate.comment_color;
468 }
469
470 hc->hc_html = html;
471
472 if (hc->hc_link) hc->hc_link = ob_strdup(hc->hc_link);
473 if (hc->hc_tag) hc->hc_tag = ob_strdup(hc->hc_tag);
474 hc->hc_next = 0;
475
476 HtmlFont_updatePoints(&hc->hc_font);
477 HtmlContext_activateFont(hc);
478
479 return hc;
480 }
481
delete_HtmlContext(HtmlContext * hc)482 static void delete_HtmlContext(HtmlContext *hc)
483 {
484 if (hc->hc_link) ob_free(hc->hc_link);
485 if (hc->hc_tag) ob_free(hc->hc_tag);
486 }
487
new_HtmlUnit(const char * text,int len,HtmlContext * hc)488 static HtmlUnit *new_HtmlUnit(const char *text,int len,HtmlContext *hc)
489 {
490 HtmlUnit *hu = OM_MALLOC(HtmlUnit);
491
492 hu->hu_type = HU_TEXT;
493 hu->hu_text = ob_malloc(len+1,"char*");
494 strncpy(hu->hu_text,text,len);
495 hu->hu_text[len] =0;
496 hu->hu_x = hu->hu_y = 0;
497 hu->hu_context = hc;
498 hu->hu_image = 0;
499
500 return hu;
501 }
502
new_HtmlUnit_T(int htype,HtmlContext * hc)503 static HtmlUnit *new_HtmlUnit_T(int htype,HtmlContext *hc)
504 {
505 HtmlUnit *hu = OM_MALLOC(HtmlUnit);
506
507 hu->hu_type = htype;
508 hu->hu_text =0;
509 hu->hu_x = hu->hu_y = 0;
510 hu->hu_width = 0;
511 hu->hu_context = hc;
512 hu->hu_image = 0;
513
514 return hu;
515 }
516
delete_HtmlUnit(HtmlUnit * hu)517 static void delete_HtmlUnit(HtmlUnit *hu)
518 {
519 if (hu->hu_image)
520 Tk_FreeImage(hu->hu_image);
521
522 if (hu->hu_text)
523 ob_free(hu->hu_text);
524 ob_free(hu);
525 }
526
new_Html(TargetDev_e target)527 Html *new_Html(TargetDev_e target)
528 {
529 Html *h = OM_MALLOC(Html);
530
531 h->h_reqWidth = 100;
532 h->h_width = h->h_reqWidth;
533 h->h_target = target;
534 h->h_height = 0;
535 h->h_dataLen = 0;
536 h->h_data = 0;
537 h->h_head = 0;
538 h->h_tail = 0;
539 h->h_context = new_HtmlContext(0,h);
540 h->h_contextPool = 0;
541 h->h_zoom = TkGate.circuit->zoom_factor;
542 h->h_locale = TkGate.circuit->c_locale;
543
544 return h;
545 }
546
delete_Html(Html * h)547 void delete_Html(Html *h)
548 {
549 HtmlUnit *u,*next_u;
550 HtmlContext *hc,*next_hc;
551
552 if (h->h_data) ob_free(h->h_data);
553
554 for (u = h->h_head;u;u = next_u) {
555 next_u = u->hu_next;
556 delete_HtmlUnit(u);
557 }
558
559 for (hc = h->h_context;hc;hc = next_hc) {
560 next_hc = hc->hc_next;
561 delete_HtmlContext(hc);
562 }
563 for (hc = h->h_contextPool;hc;hc = next_hc) {
564 next_hc = hc->hc_next;
565 delete_HtmlContext(hc);
566 }
567
568 ob_free(h);
569 }
570
Html_makeTutorialNavigationLine_bymodule(char * line)571 const char *Html_makeTutorialNavigationLine_bymodule(char *line)
572 {
573 char *p = line;
574 GModuleDef *M = TkGate.circuit->es->env;
575 int cur_pnum,max_pnum,i;
576 int spaces = 45;
577
578 for (max_pnum = 0;;) {
579 char name[STRMAX];
580
581 sprintf(name,"PAGE%d",max_pnum+1);
582 if (!env_findModule(name)) break;
583 max_pnum++;
584 }
585
586 if (max_pnum > 1) {
587 if (sscanf(M->m_name,"PAGE%d",&cur_pnum) != 1 || cur_pnum < 1 || cur_pnum > max_pnum)
588 return "[no-controls]";
589
590 if (cur_pnum == 1) {
591
592 p += sprintf(p,"<font color=gray><%s</font> ",msgLookup("tutorial.prev"));
593 } else {
594 p += sprintf(p,"<a href=\"#/PAGE%d\"><%s</a> ",cur_pnum-1,msgLookup("tutorial.prev"));
595 }
596
597 for (i = 1;i <= max_pnum;i++) {
598 if (i == cur_pnum)
599 p += sprintf(p," <b size=5>%d</b>",i);
600 else
601 p += sprintf(p," <a href=\"#/PAGE%d\">%d</b>",i,i);
602
603 if (i < 10)
604 spaces -= 2;
605 else
606 spaces -= 3;
607 }
608
609 if (cur_pnum == max_pnum)
610 p += sprintf(p," <font color=gray>%s></font>",msgLookup("tutorial.next"));
611 else
612 p += sprintf(p," <a href=\"#/PAGE%d\">%s></a>",cur_pnum+1,msgLookup("tutorial.next"));
613
614 for (i = 0;i < spaces;i++)
615 p += sprintf(p," ");
616
617 p += sprintf(p," ");
618 }
619
620 p += sprintf(p,"<a href=\"index.v\">⟨%s⟩</a> <a href=\"%s#/PAGE%d\">⟨%s⟩</a>",
621 msgLookup("tutorial.chapter"),
622 CurrentFile_path(TkGate.circuit->currentFile),cur_pnum,
623 msgLookup("tutorial.reload"));
624
625 return line;
626 }
627
Html_makeTutorialNavigationLine_byfile(char * line)628 const char *Html_makeTutorialNavigationLine_byfile(char *line)
629 {
630 char *p = line;
631 int cur_pnum,max_pnum,i;
632 int spaces = 45;
633 const char *curDirName = CurrentFile_getDir(TkGate.circuit->currentFile);
634 const char *curFileName = CurrentFile_getBase(TkGate.circuit->currentFile);
635 char baseName[STRMAX], extension[STRMAX], path[STRMAX];
636
637 if (sscanf(curFileName,"%[^0123456789]%d%s",baseName,&cur_pnum,extension) != 3 || strcmp(extension,".v") != 0)
638 return "[no-controls - invalid file name]";
639
640 #if 1
641 sprintf(path,"%s/%s",curDirName,baseName);
642 #endif
643
644 for (max_pnum = 0;;) {
645 char name[STRMAX];
646 struct stat sb;
647
648 sprintf(name,"%s%d.v",path,max_pnum+1);
649
650 if (stat(name, &sb) != 0) break;
651
652 max_pnum++;
653 }
654
655 if (max_pnum > 1) {
656 if (cur_pnum == 1) {
657 p += sprintf(p,"<font color=gray><%s</font> ",msgLookup("tutorial.prev"));
658 } else
659 p += sprintf(p,"<a href=\"%s%d.v\"><%s</a> ",baseName,cur_pnum-1,msgLookup("tutorial.prev"));
660
661 for (i = 1;i <= max_pnum;i++) {
662 if (i == cur_pnum)
663 p += sprintf(p," <b size=5>%d</b>",i);
664 else
665 p += sprintf(p," <a href=\"%s%d.v\">%d</b>",baseName,i,i);
666
667 if (i < 10)
668 spaces -= 2;
669 else
670 spaces -= 3;
671 }
672
673 if (cur_pnum == max_pnum)
674 p += sprintf(p," <font color=gray>%s></font>",msgLookup("tutorial.next"));
675 else
676 p += sprintf(p," <a href=\"%s%d.v\">%s></a>",baseName,cur_pnum+1,msgLookup("tutorial.next"));
677
678 for (i = 0;i < spaces;i++)
679 p += sprintf(p," ");
680
681 p += sprintf(p," ");
682 }
683
684 p += sprintf(p,"<a href=\"index.v\">⟨%s⟩</a> <a href=\"%s%d.v\">⟨%s⟩</a>",
685 msgLookup("tutorial.chapter"),
686 baseName,cur_pnum,
687 msgLookup("tutorial.reload"));
688
689 return line;
690 }
691
692 /*****************************************************************************
693 *
694 * Generate the HTML resulting from the <tutorial-navigation> tag.
695 *
696 *****************************************************************************/
Html_makeTutorialNavigationLine(Html * h,HtmlTag * navtag)697 const char *Html_makeTutorialNavigationLine(Html *h, HtmlTag *navtag)
698 {
699 Encoder *encoder = Html_getEncoder(h);
700 static char line[STRMAX];
701 char buf[STRMAX];
702 int byfile = 0;
703 int i;
704
705 // *buf = 0;
706
707 for (i = 0;i < navtag->ht_numOptions;i++) {
708 if (strcasecmp(navtag->ht_options[i].hto_label, "byfile") == 0)
709 byfile = istruevalue(navtag->ht_options[i].hto_value);
710 }
711
712 if (byfile)
713 Html_makeTutorialNavigationLine_byfile(buf);
714 else
715 Html_makeTutorialNavigationLine_bymodule(buf);
716
717 recodeText(encoder, line, STRMAX, buf);
718
719 return line;
720 }
721
722 /******************************************************************************
723 *
724 * Add a line of data to the html object
725 *
726 *****************************************************************************/
Html_addLine(Html * h,const char * line)727 void Html_addLine(Html *h,const char *line)
728 {
729 char buf[STRMAX];
730 /* Current # of allocated bytes */
731 int curLen = ((h->h_dataLen+DATA_STEP_SIZE-1)/DATA_STEP_SIZE)*DATA_STEP_SIZE;
732 int l;
733 Encoder *encoder = Html_getEncoder(h);
734
735 ob_touch(h);
736
737 recodeText(encoder, buf, STRMAX, line);
738 line = buf;
739
740 //
741 // This is a kludge to automatically create the tutorial navigation buttons
742 //
743 if (strncasecmp(line,"<tutorial-navigation",20) == 0) {
744 HtmlTag *navtag = Html_parseTag(line);
745
746 line = Html_makeTutorialNavigationLine(h,navtag);
747 }
748
749 l = strlen(line);
750
751
752 /*
753 * Check to see if we need to increase length of data buffer. Be sure to
754 * make space for the newline and the null character.
755 */
756 if (h->h_dataLen + l + 2 > curLen) {
757 int newLen = ((h->h_dataLen + l + 2 + DATA_STEP_SIZE-1)/DATA_STEP_SIZE)*DATA_STEP_SIZE;
758
759 if (h->h_data) {
760 h->h_data = ob_realloc(h->h_data,newLen);
761 } else
762 h->h_data = ob_malloc(newLen,"char*");
763 }
764
765 /*
766 * Append the data plus a newline.
767 */
768 strcpy(h->h_data + h->h_dataLen,line);
769 h->h_dataLen += l;
770 h->h_data[h->h_dataLen++] = '\n';
771 h->h_data[h->h_dataLen] = 0;
772 }
773
774 /******************************************************************************
775 *
776 * Delete all html units from an html object.
777 *
778 *****************************************************************************/
Html_flushUnits(Html * h)779 static void Html_flushUnits(Html *h)
780 {
781 HtmlUnit *hu,*next_hu;
782
783 ob_touch(h);
784
785 for (hu = h->h_head;hu;hu = next_hu) {
786 next_hu = hu->hu_next;
787 delete_HtmlUnit(hu);
788 }
789 h->h_head = h->h_tail = 0;
790 }
791
Html_addUnit(Html * h,HtmlUnit * hu)792 static void Html_addUnit(Html *h,HtmlUnit *hu)
793 {
794 ob_touch(h);
795
796 if (h->h_tail) {
797 h->h_tail->hu_next = hu;
798 hu->hu_prev = h->h_tail;
799 h->h_tail = hu;
800 hu->hu_next = 0;
801 } else {
802 h->h_head = h->h_tail = hu;
803 hu->hu_prev = 0;
804 hu->hu_next = 0;
805 }
806 }
807
Html_pushContext(Html * h,HtmlContext * hc)808 static void Html_pushContext(Html *h,HtmlContext *hc)
809 {
810 ob_touch(hc);
811 ob_touch(h);
812
813 hc->hc_next = h->h_context;
814 h->h_context = hc;
815 }
816
Html_popContext(Html * h)817 static void Html_popContext(Html *h)
818 {
819 HtmlContext *hc = h->h_context;
820
821 if (!hc->hc_next) return; /* Don't pop last context */
822
823 ob_touch(hc);
824 ob_touch(h);
825
826 h->h_context = h->h_context->hc_next;
827
828 hc->hc_next = h->h_contextPool;
829 h->h_contextPool = hc;
830 }
831
on_image_changed(ClientData data,int x,int y,int width,int height,int image_width,int image_height)832 static void on_image_changed(ClientData data,
833 int x, int y,
834 int width, int height,
835 int image_width, int image_height)
836 {
837 /* This space intentionally left blank. */
838 }
839
840 /*****************************************************************************
841 *
842 * Handle a <img> element.
843 *
844 *****************************************************************************/
Html_handle_img(Html * h,HtmlTag * tag)845 void Html_handle_img(Html *h, HtmlTag *tag)
846 {
847 HtmlContext *hc = new_HtmlContext(h->h_context,h);
848 HtmlUnit *hu = new_HtmlUnit_T(HU_IMAGE,hc);
849 int width = 16, height = 16;
850 const char *gifFile = "blk_copy.gif";
851 int i;
852
853 hc->hc_pixelColor = notAColor;
854
855 ob_touch(hu);
856
857 for (i = 0;i < tag->ht_numOptions;i++) {
858 if (strcasecmp(tag->ht_options[i].hto_label, "src") == 0) {
859 char buf[STRMAX];
860
861 strcpy(buf,tag->ht_options[i].hto_value);
862 expandSpecialDirs(buf);
863 gifFile = ob_strdup(buf);
864 } else if (strcasecmp(tag->ht_options[i].hto_label, "bgcolor") == 0) {
865 ob_touch(hc);
866 hc->hc_pixelColor = GatePainter_getColor(TkGate.painterW,
867 tag->ht_options[i].hto_value);
868 }
869 }
870
871 Html_pushContext(h,hc);
872 Html_popContext(h);
873 Html_addUnit(h,hu);
874
875 ob_touch(hu);
876 ob_touch(hc);
877
878 DoTcl("gifI %s",gifFile);
879 hu->hu_image = Tk_GetImage(TkGate.tcl, Tk_MainWindow(TkGate.tcl), Tcl_GetStringResult(TkGate.tcl), on_image_changed, 0);
880 if (hu->hu_image)
881 Tk_SizeOfImage(hu->hu_image, &width, &height);
882
883 hu->hu_text = 0;
884 hu->hu_width = width;
885
886 hc->hc_fontMetrics.descent = h->h_context->hc_fontMetrics.descent;
887 hc->hc_fontMetrics.ascent = height - hc->hc_fontMetrics.descent;
888 }
889
890 /*****************************************************************************
891 *
892 * Handle a <value-of> element.
893 *
894 *****************************************************************************/
Html_handle_valueOf(Html * h,HtmlTag * tag)895 void Html_handle_valueOf(Html *h, HtmlTag *tag)
896 {
897 extern char *release_date;
898 char *name = 0;
899 const char *text = 0;
900 int i;
901
902 for (i = 0;i < tag->ht_numOptions;i++)
903 if (strcasecmp(tag->ht_options[i].hto_label,"name") == 0)
904 name = tag->ht_options[i].hto_value;
905
906 if (!name) return;
907
908 if (strcasecmp(name,"tkgate-version") == 0) {
909 text = TKGATE_FULL_VERSION;
910 } else if (strcasecmp(name,"tkgate-homepage") == 0) {
911 text = PACKAGE_URL;
912 } else if (strcasecmp(name,"tkgate-mailcontact") == 0) {
913 text = PACKAGE_BUGREPORT;
914 } else if (strcasecmp(name,"tkgate-copyright") == 0) {
915 text = TKGATE_COPYRIGHT;
916 } else if (strcasecmp(name,"tkgate-release-date") == 0) {
917 text = release_date;
918 } else if (strcasecmp(name,"pancake-recipe") == 0) {
919 extern const char *recipe_list[];
920 text = recipe_list[0];
921 }
922
923 if (text)
924 Html_addUnit(h,new_HtmlUnit(text,strlen(text),h->h_context));
925 }
926
927 /*****************************************************************************
928 *
929 * Handle font modifiers in a tag
930 *
931 *****************************************************************************/
HtmlContext_handle_modifiers(HtmlContext * hc,HtmlTag * tag)932 void HtmlContext_handle_modifiers(HtmlContext *hc,HtmlTag *tag)
933 {
934 int i;
935
936 for (i = 0;i < tag->ht_numOptions;i++) {
937 const char *label = tag->ht_options[i].hto_label;
938 const char *value = tag->ht_options[i].hto_value;
939
940 if (strcasecmp(label,"face") == 0) {
941 fontfamily_t j;
942
943 ob_touch(hc);
944
945 for (j = 0;j < FF_MAX;j++)
946 if (strcasecmp(getFontFamilyName(j),value) == 0)
947 hc->hc_font.gateFont.family = j;
948 } else if (strcasecmp(label,"size") == 0) {
949 /*
950 * Scan font size. We decrement the scanned size to convert from the
951 * html specification to our internal size code.
952 */
953 if (sscanf(value,"%d",(int*)&hc->hc_font.gateFont.size) == 1)
954 --hc->hc_font.gateFont.size;
955 HtmlFont_updatePoints(&hc->hc_font);
956 } else if (strcasecmp(label,"color") == 0) {
957 ob_touch(hc);
958 if (hc->hc_html->h_target == TD_X11) {
959 hc->hc_pixelColor = GatePainter_getColor(TkGate.painterW, value);
960 }
961 else
962 hc->hc_pixelColor = GatePainter_getColor(TkGate.painterW, "balack");
963 }
964 }
965 }
966
967
968 /*****************************************************************************
969 *
970 * Handler for basic open/close html tags which modify the context.
971 *
972 *****************************************************************************/
Html_handle_basic(Html * h,HtmlTag * tag)973 void Html_handle_basic(Html *h, HtmlTag *tag)
974 {
975 HtmlContext *hc = new_HtmlContext(h->h_context,h);
976
977 ob_touch(hc);
978 if (strcasecmp(tag->ht_name,"b") == 0) {
979 hc->hc_font.gateFont.prop |= FP_BOLD;
980 HtmlContext_handle_modifiers(hc,tag);
981 } else if (strcasecmp(tag->ht_name,"i") == 0) {
982 hc->hc_font.gateFont.prop |= FP_ITALIC;
983 HtmlContext_handle_modifiers(hc,tag);
984 } else if (strcasecmp(tag->ht_name,"tt") == 0) {
985 hc->hc_font.gateFont.family = FF_COURIER;
986 HtmlContext_handle_modifiers(hc,tag);
987 } else if (strcasecmp(tag->ht_name,"big") == 0) {
988 if (hc->hc_font.gateFont.size + 1 < FS_MAX)
989 ++hc->hc_font.gateFont.size;
990 HtmlFont_updatePoints(&hc->hc_font);
991 HtmlContext_handle_modifiers(hc,tag);
992 } else if (strcasecmp(tag->ht_name,"small") == 0) {
993 if (hc->hc_font.gateFont.size - 1 >= 0)
994 --hc->hc_font.gateFont.size;
995 HtmlFont_updatePoints(&hc->hc_font);
996 HtmlContext_handle_modifiers(hc,tag);
997 } else if (strcasecmp(tag->ht_name,"font") == 0) {
998 HtmlContext_handle_modifiers(hc,tag);
999 }
1000
1001 HtmlContext_activateFont(hc);
1002 Html_pushContext(h,hc);
1003 }
1004
1005 /*****************************************************************************
1006 *
1007 * Handler for heading tags
1008 *
1009 *****************************************************************************/
Html_handle_heading(Html * h,HtmlTag * tag)1010 void Html_handle_heading(Html *h, HtmlTag *tag)
1011 {
1012 HtmlContext *hc = new_HtmlContext(h->h_context,h);
1013
1014 Html_addUnit(h,new_HtmlUnit_T(HU_BREAK,h->h_context));
1015
1016 ob_touch(hc);
1017
1018 hc->hc_font.gateFont.prop |= FP_BOLD;
1019
1020 if (strcasecmp(tag->ht_name,"h1") == 0) {
1021 hc->hc_font.gateFont.size = FS_XHUGE;
1022 } else if (strcasecmp(tag->ht_name,"h2") == 0) {
1023 hc->hc_font.gateFont.size = FS_HUGE;
1024 } else if (strcasecmp(tag->ht_name,"h3") == 0) {
1025 hc->hc_font.gateFont.size = FS_LARGE;
1026 }
1027
1028 HtmlFont_updatePoints(&hc->hc_font);
1029
1030 HtmlContext_handle_modifiers(hc,tag);
1031
1032 HtmlContext_activateFont(hc);
1033 Html_pushContext(h,hc);
1034 }
1035
Html_handle_a(Html * h,HtmlTag * ht)1036 void Html_handle_a(Html *h, HtmlTag *ht)
1037 {
1038 HtmlContext *hc = new_HtmlContext(h->h_context,h);
1039 int i;
1040
1041 ob_touch(hc);
1042 hc->hc_pixelColor = TkGate.hyperlink_color;
1043 HtmlContext_activateFont(hc);
1044
1045 for (i = 0;i < ht->ht_numOptions;i++) {
1046 if (strcasecmp(ht->ht_options[i].hto_label, "href") == 0) {
1047 hc->hc_link = ob_strdup(ht->ht_options[i].hto_value);
1048 } else if (strcasecmp(ht->ht_options[i].hto_label, "name") == 0) {
1049 hc->hc_tag = ob_strdup(ht->ht_options[i].hto_value);
1050 }
1051 }
1052
1053 Html_pushContext(h,hc);
1054 }
1055
1056 /*****************************************************************************
1057 *
1058 * Process the html tag that starts at 'tag' and is 'len' characters long.
1059 *
1060 *****************************************************************************/
Html_processTag(Html * h,const char * tag,int len)1061 static void Html_processTag(Html *h, const char *tag,int len)
1062 {
1063 HtmlTag *ht;
1064 int i;
1065
1066 ht = Html_parseTag(tag);
1067 if (!ht) return;
1068
1069
1070 if (*ht->ht_name == '/') {
1071 Html_popContext(h);
1072 return;
1073 }
1074
1075 for (i = 0;htmlHandlers[i].tag;i++)
1076 if (strcasecmp(ht->ht_name,htmlHandlers[i].tag) == 0)
1077 (*htmlHandlers[i].func)(h,ht);
1078 }
1079
1080 /*****************************************************************************
1081 *
1082 * Process character entity references.
1083 *
1084 *****************************************************************************/
Html_processSpecialChar(Html * h,char * spec,int len)1085 static void Html_processSpecialChar(Html *h,char *spec,int len)
1086 {
1087 char *text = 0;
1088 char *pretag = 0;
1089 char *posttag = 0;
1090 int i;
1091
1092 for (i = 0;htmlSpecialSpecs[i].spec;i++) {
1093 if (strncmp(spec,htmlSpecialSpecs[i].spec,len) == 0) {
1094 text = htmlSpecialSpecs[i].text;
1095 pretag = htmlSpecialSpecs[i].pretag;
1096 posttag = htmlSpecialSpecs[i].posttag;
1097 }
1098 }
1099
1100 if (text) {
1101 if (pretag) Html_processTag(h,pretag,strlen(pretag));
1102 Html_addUnit(h,new_HtmlUnit(text,strlen(text),h->h_context));
1103 if (posttag) Html_processTag(h,posttag,strlen(posttag));
1104 } else
1105 Html_addUnit(h,new_HtmlUnit(spec,len,h->h_context));
1106 }
1107
1108
1109 /******************************************************************************
1110 *
1111 * Parition an html object into pieces. Each piece is one of:
1112 * * An html tag
1113 * * An html special character sequence
1114 * * A new line
1115 * * A text string
1116 *
1117 *****************************************************************************/
Html_partition(Html * h,char * data)1118 void Html_partition(Html *h,char *data)
1119 {
1120 char *p,*q;
1121 Encoder *encoder = Html_getEncoder(h);
1122 int isJapanese = isJapaneseDisplay(encoder);
1123 int i;
1124 HtmlUnit *hu;
1125 HtmlContext *hc;
1126
1127 Html_flushUnits(h);
1128
1129 if (!data) return; /* No data */
1130
1131 p = q = data;
1132 while (*p) {
1133 if (*p == '<') {
1134 /******************************************************************
1135 *
1136 * This is an html tag. Include everything up to end of tag.
1137 *
1138 ******************************************************************/
1139 q = strchr(p,'>');
1140 if (!q) q = p + strlen(p);
1141
1142 Html_processTag(h,p,q-p+1);
1143 if (!*q) break;
1144 p = q+1;
1145 } else if (*p == '&') {
1146 /******************************************************************
1147 *
1148 * This is special character. Parse until ';' and convert to the
1149 * specified character.
1150 *
1151 ******************************************************************/
1152 q = strchr(p,';');
1153 if (!q) q = p + strlen(p);
1154
1155 Html_processSpecialChar(h,p,q-p+1);
1156 if (!*q) break;
1157 p = q+1;
1158 } else if (*p == '\n') {
1159 /******************************************************************
1160 *
1161 * This is newline. Insert a newline unit if we are in preformat
1162 * mode, otherwise just insert a space.
1163 *
1164 ******************************************************************/
1165 if (h->h_context->hc_preformat) {
1166 hu = new_HtmlUnit_T(HU_NEWLINE,h->h_context);
1167 Html_addUnit(h,hu);
1168 } else
1169 Html_addUnit(h,new_HtmlUnit(" ",1,h->h_context));
1170 p++;
1171 } else if ((*p & 0x80) && isJapanese) {
1172 /******************************************************************
1173 *
1174 * This is Kanji text. Scan until we find a non-8-bit character.
1175 *
1176 ******************************************************************/
1177
1178 for (q = p;*q;q++)
1179 if (!(*q & 0x80)) break;
1180
1181 hc = new_HtmlContext(h->h_context,h);
1182 hc->hc_font.gateFont.family = FF_KANJI;
1183 HtmlFont_updatePoints(&hc->hc_font);
1184 HtmlContext_activateFont(hc);
1185 Html_pushContext(h,hc);
1186 Html_popContext(h);
1187 hu = new_HtmlUnit(p,q-p,hc);
1188 Html_addUnit(h,hu);
1189 for (i = 0;hu->hu_text[i];i++) hu->hu_text[i] &= 0x7f;
1190
1191 if (!*q) break;
1192 p = q;
1193 } else {
1194 /******************************************************************
1195 *
1196 * This is a regular non-kanji text string
1197 *
1198 ******************************************************************/
1199
1200 for (q = p;*q;q++)
1201 if (strchr("<&\n",*q) != 0 || ((*q & 0x80) && isJapanese))
1202 break;
1203 hu = new_HtmlUnit(p,q-p,h->h_context);
1204 Html_addUnit(h,hu);
1205
1206 if (!*q) break;
1207 p = q;
1208 }
1209 }
1210 }
1211
1212 /******************************************************************************
1213 *
1214 * Format an html object.
1215 *
1216 * Parameters:
1217 * h Html object to be formatted.
1218 *
1219 *****************************************************************************/
Html_format(Html * h)1220 void Html_format(Html *h)
1221 {
1222 HtmlUnit *hu,*hu2;
1223 HtmlUnit *linestart_hu;
1224 int max_ascent = 0;
1225 int max_descent = 0;
1226 int max_width = 0;
1227 int x = 0, y = 0; /* Current text position */
1228 char *p;
1229
1230 ob_touch(h);
1231 Html_partition(h,h->h_data);
1232
1233 h->h_isVisible = 0;
1234
1235 linestart_hu = h->h_head; /* This is the text unit for the current line */
1236 for (hu = h->h_head;hu;hu = hu->hu_next) {
1237 HtmlContext *hc = hu->hu_context; /* Get context of this unit */
1238
1239 /*
1240 * Update line height metrics
1241 */
1242 if (HtmlContext_fontAscent(hc) > max_ascent)
1243 max_ascent = HtmlContext_fontAscent(hc);
1244 if (HtmlContext_fontDescent(hc) > max_descent)
1245 max_descent = HtmlContext_fontDescent(hc);
1246
1247 switch (hu->hu_type) {
1248 case HU_BREAK :
1249 if (x == 0) break; /* Do a newline only if we are not already on a newline */
1250 /* fall through */
1251 case HU_NEWLINE :
1252 y += max_ascent;
1253 for (hu2 = linestart_hu;hu2 != hu;hu2 = hu2->hu_next) {
1254 ob_touch(hu2);
1255 hu2->hu_y = y;
1256 }
1257 y += max_descent;
1258 x = 0;
1259
1260 /*
1261 * Reset metrics and advance linestart_hu to start of next line.
1262 */
1263 max_ascent = 0;
1264 max_descent = 0;
1265 linestart_hu = hu->hu_next;
1266 break;
1267 case HU_TEXT :
1268 ob_touch(hu);
1269
1270 hu->hu_x = x;
1271
1272 hu->hu_width = HtmlContext_stringWidth(hc,hu->hu_text,strlen(hu->hu_text));
1273
1274 x += hu->hu_width;
1275 if (x > max_width) max_width = x;
1276
1277 if (!h->h_isVisible) {
1278 for (p = hu->hu_text;*p;p++)
1279 if (!isspace(*p)) {
1280 h->h_isVisible = 1;
1281 }
1282 }
1283 break;
1284 case HU_IMAGE :
1285 hu->hu_x = x;
1286 x += hu->hu_width;
1287 if (x > max_width) max_width = x;
1288 h->h_isVisible = 1;
1289 break;
1290 default:
1291 /* For cases like HU_RULE */
1292 break;
1293 }
1294 }
1295
1296 h->h_width = max_width;
1297 h->h_height = y + max_descent;
1298 }
1299
Html_psPrint(Html * h,GPrint * P,int x,int y)1300 void Html_psPrint(Html *h,GPrint *P,int x,int y)
1301 {
1302 HtmlUnit *hu;
1303 HtmlContext *last_hc = 0;
1304 /** @TODO to remove */
1305 /* Encoder *encoder = Circuit_getPSEncoder(TkGate.circuit); */
1306 /* char text[STRMAX]; */
1307
1308 for (hu = h->h_head;hu;hu = hu->hu_next) {
1309 HtmlContext *hc = hu->hu_context; /* Get context of this unit */
1310
1311 /*
1312 * Update properties only if there was a change.
1313 */
1314 if (hc != last_hc) {
1315 /** @TODO to remove */
1316 /* Tkg_changeColor(gc, GXxor, hc->hc_pixel); */
1317 last_hc = hc;
1318 }
1319
1320 switch (hu->hu_type) {
1321 case HU_TEXT :
1322 PSDrawText(P,&hc->hc_font,hu->hu_x + x,hu->hu_y + y,hu->hu_text,AtBaseline|AtLeft);
1323 break;
1324 case HU_IMAGE :
1325 break;
1326 default :
1327 break;
1328 }
1329 }
1330 }
1331
1332 /*****************************************************************************
1333 *
1334 * Draw a block of html text.
1335 *
1336 *****************************************************************************/
Html_draw(Html * h,int x,int y)1337 void Html_draw(Html *h,int x,int y)
1338 {
1339 GC gc = GatePainterContext_gc(TkGate.commentContext);
1340 GC igc = TkGate.imageGC;
1341 HtmlUnit *hu;
1342 HtmlContext *last_hc = 0;
1343
1344 GatePainterContext_print(TkGate.commentContext, stdout);
1345
1346 x = ctow_x(x)*TkGate.circuit->zoom_factor;
1347 y = ctow_y(y)*TkGate.circuit->zoom_factor;
1348
1349 #ifdef DEBUG
1350 Locale_print(h->h_locale, stdout);
1351 #endif
1352
1353 for (hu = h->h_head;hu;hu = hu->hu_next) {
1354 HtmlContext *hc = hu->hu_context; /* Get context of this unit */
1355 #ifdef DEBUG
1356 HtmlContext_print(hc,stdout);
1357 #endif
1358 switch (hu->hu_type) {
1359 case HU_TEXT :
1360 /*
1361 * Update properties only if there was a change.
1362 */
1363 if (hc != last_hc) {
1364 GatePainterContext_setFont(TkGate.commentContext, hc->hc_font.gateFont);
1365
1366 if (!GateColor_equals(&hc->hc_pixelColor, ¬AColor)) {
1367 Tkg_changeColor(gc, GXxor, hc->hc_pixelColor.xColor.pixel);
1368 GatePainterContext_setColor(TkGate.commentContext, hc->hc_pixelColor);
1369 }
1370 last_hc = hc;
1371 }
1372
1373 if (hc->hc_font.gateFont.family == FF_KANJI) {
1374 XDrawString16( TkGate.D,
1375 TkGate.W,
1376 gc,
1377 hu->hu_x + x,hu->hu_y + y,
1378 (XChar2b*)hu->hu_text,
1379 strlen(hu->hu_text)/2 );
1380 }/* else if (strcmp(h->h_locale->l_encDisplay, "utf-8") == 0) {
1381 XCreateFontSet
1382 Xutf8DrawString(TkGate.D, TkGate.W,gc, XFontSet
1383 hu->hu_x + x,hu->hu_y + y,
1384 (XChar2b*)hu->hu_text,
1385 strlen(hu->hu_text)/2 );
1386 } */else {
1387 GatePainter_drawString_new( TkGate.painterW,
1388 TkGate.commentContext,
1389 hu->hu_x + x, hu->hu_y + y,
1390 hu->hu_text,
1391 strlen(hu->hu_text) );
1392 }
1393
1394 break;
1395 case HU_IMAGE :
1396 {
1397 int width = hu->hu_width;
1398 int height = HtmlContext_fontAscent(hc) + HtmlContext_fontDescent(hc);
1399 int base_x = hu->hu_x + x;
1400 int base_y = hu->hu_y + y - HtmlContext_fontAscent(hc);
1401
1402 if (!GateColor_equals(&hc->hc_pixelColor, ¬AColor)) {
1403 XSetForeground(TkGate.D,igc,hc->hc_pixelColor.xColor.pixel);
1404 ZFillRectangle(TkGate.D,TkGate.W,igc, base_x,base_y, width, height);
1405 } else
1406 XSetForeground(TkGate.D,igc,XWhitePixelOfScreen(TkGate.S));
1407 Tk_RedrawImage(hu->hu_image, 0, 0, width, height, TkGate.W, base_x, base_y);
1408 }
1409 break;
1410 default :
1411 break;
1412 }
1413 }
1414 }
1415
Html_isHit(Html * h,int x,int y)1416 int Html_isHit(Html *h,int x,int y)
1417 {
1418 HtmlUnit *hu;
1419
1420 for (hu = h->h_head;hu;hu = hu->hu_next) {
1421 HtmlContext *hc = hu->hu_context; /* Get context of this unit */
1422
1423 if ((x >= hu->hu_x) &&
1424 (x <= (hu->hu_x + hu->hu_width)) &&
1425 (y <= (hu->hu_y + HtmlContext_fontDescent(hc))) &&
1426 (y >= (hu->hu_y - HtmlContext_fontAscent(hc))))
1427 return 1;
1428 }
1429
1430 return 0;
1431 }
1432
Html_getLink(Html * h,int x,int y)1433 const char *Html_getLink(Html *h,int x,int y)
1434 {
1435 HtmlUnit *hu;
1436
1437 for (hu = h->h_head;hu;hu = hu->hu_next) {
1438 HtmlContext *hc = hu->hu_context; /* Get context of this unit */
1439
1440 if (!hc->hc_link) continue;
1441
1442 if (hu->hu_type == HU_IMAGE) {
1443 int width = hu->hu_width;
1444 int height = HtmlContext_fontAscent(hc) + HtmlContext_fontDescent(hc);
1445 int base_x = hu->hu_x;
1446 int base_y = hu->hu_y - HtmlContext_fontAscent(hc);
1447
1448 if (x >= base_x && x <= (base_x + width)
1449 && y >= base_y && y <= (base_y + height)) {
1450
1451 return hc->hc_link;
1452 }
1453 } else {
1454 if (x >= hu->hu_x && x <= (hu->hu_x + hu->hu_width)
1455 && y <= (hu->hu_y + HtmlContext_fontDescent(hc)) && y >=
1456 (hu->hu_y - HtmlContext_fontAscent(hc))) {
1457
1458 return hc->hc_link;
1459 }
1460 }
1461 }
1462
1463 return 0;
1464 }
1465
HtmlContext_print(const HtmlContext * context,FILE * fp)1466 void HtmlContext_print(const HtmlContext * context, FILE * fp)
1467 {
1468 fputs("Html context: ", fp);
1469
1470 fprintf(fp, "\tassociated hyperlink:%s\n", context->hc_link);
1471 fprintf(fp, "\tassociated tag: %s\n", context->hc_tag);
1472 fprintf(fp, "\tpreformat: %d\n", context->hc_preformat);
1473 fprintf(fp, "\tis 16 bit: %d\n", context->hc_is16bit);
1474 fprintf(fp, "\tspace width: %d\n", context->hc_spaceWidth);
1475 }
1476