blob: 1b31a898277f418103bba283a00daa0d590d5177 [file] [log] [blame]
DRC2ff39b82011-07-28 08:38:59 +00001//
2// "$Id: Fl_Help_View.cxx 8063 2010-12-19 21:20:10Z matt $"
3//
4// Fl_Help_View widget routines.
5//
6// Copyright 1997-2010 by Easy Software Products.
7// Image support by Matthias Melcher, Copyright 2000-2009.
8//
9// This library is free software; you can redistribute it and/or
10// modify it under the terms of the GNU Library General Public
11// License as published by the Free Software Foundation; either
12// version 2 of the License, or (at your option) any later version.
13//
14// This library is distributed in the hope that it will be useful,
15// but WITHOUT ANY WARRANTY; without even the implied warranty of
16// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17// Library General Public License for more details.
18//
19// You should have received a copy of the GNU Library General Public
20// License along with this library; if not, write to the Free Software
21// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22// USA.
23//
24// Please report all bugs and problems on the following page:
25//
26// http://www.fltk.org/str.php
27//
28// Contents:
29//
30// Fl_Help_View::add_block() - Add a text block to the list.
31// Fl_Help_View::add_link() - Add a new link to the list.
32// Fl_Help_View::add_target() - Add a new target to the list.
33// Fl_Help_View::compare_targets() - Compare two targets.
34// Fl_Help_View::do_align() - Compute the alignment for a line in
35// a block.
36// Fl_Help_View::draw() - Draw the Fl_Help_View widget.
37// Fl_Help_View::format() - Format the help text.
38// Fl_Help_View::format_table() - Format a table...
39// Fl_Help_View::free_data() - Free memory used for the document.
40// Fl_Help_View::get_align() - Get an alignment attribute.
41// Fl_Help_View::get_attr() - Get an attribute value from the string.
42// Fl_Help_View::get_color() - Get an alignment attribute.
43// Fl_Help_View::handle() - Handle events in the widget.
44// Fl_Help_View::Fl_Help_View() - Build a Fl_Help_View widget.
45// Fl_Help_View::~Fl_Help_View() - Destroy a Fl_Help_View widget.
46// Fl_Help_View::load() - Load the specified file.
47// Fl_Help_View::resize() - Resize the help widget.
48// Fl_Help_View::topline() - Set the top line to the named target.
49// Fl_Help_View::topline() - Set the top line by number.
50// Fl_Help_View::value() - Set the help text directly.
51// scrollbar_callback() - A callback for the scrollbar.
52//
53
54//
55// Include necessary header files...
56//
57
58#include <FL/Fl_Help_View.H>
59#include <FL/Fl_Window.H>
60#include <FL/Fl_Pixmap.H>
61#include <FL/x.H>
62#include <stdio.h>
63#include <stdlib.h>
64#include <FL/fl_utf8.h>
65#include <FL/filename.H> // fl_open_uri()
66#include "flstring.h"
67#include <ctype.h>
68#include <errno.h>
69#include <math.h>
70
71#if defined(WIN32) && ! defined(__CYGWIN__)
72# include <io.h>
73# include <direct.h>
74#else
75# include <unistd.h>
76#endif // WIN32
77
78#define MAX_COLUMNS 200
79
80
81//
82// Typedef the C API sort function type the only way I know how...
83//
84
85extern "C"
86{
87 typedef int (*compare_func_t)(const void *, const void *);
88}
89
90
91//
92// Local functions...
93//
94
95static int quote_char(const char *);
96static void scrollbar_callback(Fl_Widget *s, void *);
97static void hscrollbar_callback(Fl_Widget *s, void *);
98
99//
100// global flag for image loading (see get_image).
101//
102
103static char initial_load = 0;
104
105//
106// Broken image...
107//
108
109static const char *broken_xpm[] =
110 {
111 "16 24 4 1",
112 "@ c #000000",
113 " c #ffffff",
114 "+ c none",
115 "x c #ff0000",
116 // pixels
117 "@@@@@@@+++++++++",
118 "@ @++++++++++",
119 "@ @+++++++++++",
120 "@ @++@++++++++",
121 "@ @@+++++++++",
122 "@ @+++@+++++",
123 "@ @++@@++++@",
124 "@ xxx @@ @++@@",
125 "@ xxx xx@@ @",
126 "@ xxx xxx @",
127 "@ xxxxxx @",
128 "@ xxxx @",
129 "@ xxxxxx @",
130 "@ xxx xxx @",
131 "@ xxx xxx @",
132 "@ xxx xxx @",
133 "@ @",
134 "@ @",
135 "@ @",
136 "@ @",
137 "@ @",
138 "@ @",
139 "@ @",
140 "@@@@@@@@@@@@@@@@",
141 NULL
142 };
143
144static Fl_Pixmap broken_image(broken_xpm);
145
146//
147// Simple margin stack for Fl_Help_View::format()...
148//
149
150struct fl_margins {
151 int depth_;
152 int margins_[100];
153
154 fl_margins() { clear(); }
155
156 int clear() {
157// puts("fl_margins::clear()");
158
159 depth_ = 0;
160 return margins_[0] = 4;
161 }
162
163 int current() { return margins_[depth_]; }
164
165 int pop() {
166// printf("fl_margins::pop(): depth_=%d, xx=%d\n", depth_,
167// depth_ > 0 ? margins_[depth_ - 1] : 4);
168
169 if (depth_ > 0) {
170 depth_ --;
171 return margins_[depth_];
172 } else return 4;
173 }
174
175 int push(int indent) {
176 int xx;
177
178 xx = margins_[depth_] + indent;
179
180// printf("fl_margins::push(indent=%d): depth_=%d, xx=%d\n", indent,
181// depth_ + 1, xx);
182
183 if (depth_ < 99) {
184 depth_ ++;
185 margins_[depth_] = xx;
186 }
187
188 return xx;
189 }
190};
191
192//
193// All the stuff needed to implement text selection in Fl_Help_View
194//
195
196/* matt:
197 * We are trying to keep binary compatibility with previous versions
198 * of FLTK. This means that we are limited to adding static variables
199 * only to not enlarge the Fl_Help_View class. Lucky for us, only one
200 * text can be selected system wide, so we can remember the selection
201 * in a single set of variables.
202 *
203 * Still to do:
204 * - &word; style characters mess up our count inside a word boundary
205 * - we can only select words, no individual characters
206 * - no dragging of the selection into another widget
207 * - selection must be cleared if another widget get focus!
208 * - write a comment for every new function
209 */
210
211/*
212The following functions are also used to draw stuff and should be replaced with
213local copies that are much faster when merely counting:
214
215fl_color(Fl_Color);
216fl_rectf(int, int, int, int);
217fl_push_clip(int, int, int, int);
218fl_xyline(int, int, int);
219fl_rect()
220fl_line()
221img->draw()
222*/
223
224// We don't put the offscreen buffer in the help view class because
225// we'd need to include x.H in the header...
226static Fl_Offscreen fl_help_view_buffer;
227int Fl_Help_View::selection_first = 0;
228int Fl_Help_View::selection_last = 0;
229int Fl_Help_View::selection_push_first = 0;
230int Fl_Help_View::selection_push_last = 0;
231int Fl_Help_View::selection_drag_first = 0;
232int Fl_Help_View::selection_drag_last = 0;
233int Fl_Help_View::selected = 0;
234int Fl_Help_View::draw_mode = 0;
235int Fl_Help_View::mouse_x = 0;
236int Fl_Help_View::mouse_y = 0;
237int Fl_Help_View::current_pos = 0;
238Fl_Help_View *Fl_Help_View::current_view = 0L;
239Fl_Color Fl_Help_View::hv_selection_color;
240Fl_Color Fl_Help_View::hv_selection_text_color;
241
242/*
243 * Limitation: if a word contains &code; notations, we will calculate a wrong length.
244 *
245 * This function must be optimized for speed!
246 */
247void Fl_Help_View::hv_draw(const char *t, int x, int y)
248{
249 if (selected && current_view==this && current_pos<selection_last && current_pos>=selection_first) {
250 Fl_Color c = fl_color();
251 fl_color(hv_selection_color);
252 int w = (int)fl_width(t);
253 if (current_pos+(int)strlen(t)<selection_last)
254 w += (int)fl_width(' ');
255 fl_rectf(x, y+fl_descent()-fl_height(), w, fl_height());
256 fl_color(hv_selection_text_color);
257 fl_draw(t, x, y);
258 fl_color(c);
259 } else {
260 fl_draw(t, x, y);
261 }
262 if (draw_mode) {
263 int w = (int)fl_width(t);
264 if (mouse_x>=x && mouse_x<x+w) {
265 if (mouse_y>=y-fl_height()+fl_descent()&&mouse_y<=y+fl_descent()) {
266 int f = current_pos;
267 int l = f+strlen(t); // use 'quote_char' to calculate the true length of the HTML string
268 if (draw_mode==1) {
269 selection_push_first = f;
270 selection_push_last = l;
271 } else {
272 selection_drag_first = f;
273 selection_drag_last = l;
274 }
275 }
276 }
277 }
278}
279
280
281/** Adds a text block to the list. */
282Fl_Help_Block * // O - Pointer to new block
283Fl_Help_View::add_block(const char *s, // I - Pointer to start of block text
284 int xx, // I - X position of block
285 int yy, // I - Y position of block
286 int ww, // I - Right margin of block
287 int hh, // I - Height of block
288 unsigned char border) // I - Draw border?
289{
290 Fl_Help_Block *temp; // New block
291
292
293// printf("add_block(s = %p, xx = %d, yy = %d, ww = %d, hh = %d, border = %d)\n",
294// s, xx, yy, ww, hh, border);
295
296 if (nblocks_ >= ablocks_)
297 {
298 ablocks_ += 16;
299
300 if (ablocks_ == 16)
301 blocks_ = (Fl_Help_Block *)malloc(sizeof(Fl_Help_Block) * ablocks_);
302 else
303 blocks_ = (Fl_Help_Block *)realloc(blocks_, sizeof(Fl_Help_Block) * ablocks_);
304 }
305
306 temp = blocks_ + nblocks_;
307 memset(temp, 0, sizeof(Fl_Help_Block));
308 temp->start = s;
309 temp->end = s;
310 temp->x = xx;
311 temp->y = yy;
312 temp->w = ww;
313 temp->h = hh;
314 temp->border = border;
315 temp->bgcolor = bgcolor_;
316 nblocks_ ++;
317
318 return (temp);
319}
320
321
322/** Adds a new link to the list. */
323void Fl_Help_View::add_link(const char *n, // I - Name of link
324 int xx, // I - X position of link
325 int yy, // I - Y position of link
326 int ww, // I - Width of link text
327 int hh) // I - Height of link text
328{
329 Fl_Help_Link *temp; // New link
330 char *target; // Pointer to target name
331
332
333 if (nlinks_ >= alinks_)
334 {
335 alinks_ += 16;
336
337 if (alinks_ == 16)
338 links_ = (Fl_Help_Link *)malloc(sizeof(Fl_Help_Link) * alinks_);
339 else
340 links_ = (Fl_Help_Link *)realloc(links_, sizeof(Fl_Help_Link) * alinks_);
341 }
342
343 temp = links_ + nlinks_;
344
345 temp->x = xx;
346 temp->y = yy;
347 temp->w = xx + ww;
348 temp->h = yy + hh;
349
350 strlcpy(temp->filename, n, sizeof(temp->filename));
351
352 if ((target = strrchr(temp->filename, '#')) != NULL)
353 {
354 *target++ = '\0';
355 strlcpy(temp->name, target, sizeof(temp->name));
356 }
357 else
358 temp->name[0] = '\0';
359
360 nlinks_ ++;
361}
362
363
364/** Adds a new target to the list. */
365void Fl_Help_View::add_target(const char *n, // I - Name of target
366 int yy) // I - Y position of target
367{
368 Fl_Help_Target *temp; // New target
369
370
371 if (ntargets_ >= atargets_)
372 {
373 atargets_ += 16;
374
375 if (atargets_ == 16)
376 targets_ = (Fl_Help_Target *)malloc(sizeof(Fl_Help_Target) * atargets_);
377 else
378 targets_ = (Fl_Help_Target *)realloc(targets_, sizeof(Fl_Help_Target) * atargets_);
379 }
380
381 temp = targets_ + ntargets_;
382
383 temp->y = yy;
384 strlcpy(temp->name, n, sizeof(temp->name));
385
386 ntargets_ ++;
387}
388
389/** Compares two targets.*/
390int // O - Result of comparison
391Fl_Help_View::compare_targets(const Fl_Help_Target *t0, // I - First target
392 const Fl_Help_Target *t1) // I - Second target
393{
394 return (strcasecmp(t0->name, t1->name));
395}
396
397/** Computes the alignment for a line in a block.*/
398int // O - New line
399Fl_Help_View::do_align(Fl_Help_Block *block, // I - Block to add to
400 int line, // I - Current line
401 int xx, // I - Current X position
402 int a, // I - Current alignment
403 int &l) // IO - Starting link
404{
405 int offset; // Alignment offset
406
407
408 switch (a)
409 {
410 case RIGHT : // Right align
411 offset = block->w - xx;
412 break;
413 case CENTER : // Center
414 offset = (block->w - xx) / 2;
415 break;
416 default : // Left align
417 offset = 0;
418 break;
419 }
420
421 block->line[line] = block->x + offset;
422
423 if (line < 31)
424 line ++;
425
426 while (l < nlinks_)
427 {
428 links_[l].x += offset;
429 links_[l].w += offset;
430 l ++;
431 }
432
433 return (line);
434}
435
436/** Draws the Fl_Help_View widget. */
437void
438Fl_Help_View::draw()
439{
440 int i; // Looping var
441 const Fl_Help_Block *block; // Pointer to current block
442 const char *ptr, // Pointer to text in block
443 *attrs; // Pointer to start of element attributes
444 char *s, // Pointer into buffer
445 buf[1024], // Text buffer
446 attr[1024]; // Attribute buffer
447 int xx, yy, ww, hh; // Current positions and sizes
448 int line; // Current line
449 Fl_Font font;
450 Fl_Fontsize fsize; // Current font and size
451 Fl_Color fcolor; // current font color
452 int head, pre, // Flags for text
453 needspace; // Do we need whitespace?
454 Fl_Boxtype b = box() ? box() : FL_DOWN_BOX;
455 // Box to draw...
456 int underline, // Underline text?
457 xtra_ww; // Extra width for underlined space between words
458
459 // Draw the scrollbar(s) and box first...
460 ww = w();
461 hh = h();
462 i = 0;
463
464 draw_box(b, x(), y(), ww, hh, bgcolor_);
465
466 if ( hscrollbar_.visible() || scrollbar_.visible() ) {
467 int scrollsize = scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size();
468 int hor_vis = hscrollbar_.visible();
469 int ver_vis = scrollbar_.visible();
470 // Scrollbar corner
471 int scorn_x = x() + ww - (ver_vis?scrollsize:0) - Fl::box_dw(b) + Fl::box_dx(b);
472 int scorn_y = y() + hh - (hor_vis?scrollsize:0) - Fl::box_dh(b) + Fl::box_dy(b);
473 if ( hor_vis ) {
474 if ( hscrollbar_.h() != scrollsize ) { // scrollsize changed?
475 hscrollbar_.resize(x(), scorn_y, scorn_x - x(), scrollsize);
476 init_sizes();
477 }
478 draw_child(hscrollbar_);
479 hh -= scrollsize;
480 }
481 if ( ver_vis ) {
482 if ( scrollbar_.w() != scrollsize ) { // scrollsize changed?
483 scrollbar_.resize(scorn_x, y(), scrollsize, scorn_y - y());
484 init_sizes();
485 }
486 draw_child(scrollbar_);
487 ww -= scrollsize;
488 }
489 if ( hor_vis && ver_vis ) {
490 // Both scrollbars visible? Draw little gray box in corner
491 fl_color(FL_GRAY);
492 fl_rectf(scorn_x, scorn_y, scrollsize, scrollsize);
493 }
494 }
495
496 if (!value_)
497 return;
498
499 if (current_view == this && selected) {
500 hv_selection_color = FL_SELECTION_COLOR;
501 hv_selection_text_color = fl_contrast(textcolor_, FL_SELECTION_COLOR);
502 }
503 current_pos = 0;
504
505 // Clip the drawing to the inside of the box...
506 fl_push_clip(x() + Fl::box_dx(b), y() + Fl::box_dy(b),
507 ww - Fl::box_dw(b), hh - Fl::box_dh(b));
508 fl_color(textcolor_);
509
510 // Draw all visible blocks...
511 for (i = 0, block = blocks_; i < nblocks_; i ++, block ++)
512 if ((block->y + block->h) >= topline_ && block->y < (topline_ + h()))
513 {
514 line = 0;
515 xx = block->line[line];
516 yy = block->y - topline_;
517 hh = 0;
518 pre = 0;
519 head = 0;
520 needspace = 0;
521 underline = 0;
522
523 initfont(font, fsize, fcolor);
524
525 for (ptr = block->start, s = buf; ptr < block->end;)
526 {
527 if ((*ptr == '<' || isspace((*ptr)&255)) && s > buf)
528 {
529 if (!head && !pre)
530 {
531 // Check width...
532 *s = '\0';
533 s = buf;
534 ww = (int)fl_width(buf);
535
536 if (needspace && xx > block->x)
537 xx += (int)fl_width(' ');
538
539 if ((xx + ww) > block->w)
540 {
541 if (line < 31)
542 line ++;
543 xx = block->line[line];
544 yy += hh;
545 hh = 0;
546 }
547
548 hv_draw(buf, xx + x() - leftline_, yy + y());
549 if (underline) {
550 xtra_ww = isspace((*ptr)&255)?(int)fl_width(' '):0;
551 fl_xyline(xx + x() - leftline_, yy + y() + 1,
552 xx + x() - leftline_ + ww + xtra_ww);
553 }
554 current_pos = ptr-value_;
555
556 xx += ww;
557 if ((fsize + 2) > hh)
558 hh = fsize + 2;
559
560 needspace = 0;
561 }
562 else if (pre)
563 {
564 while (isspace((*ptr)&255))
565 {
566 if (*ptr == '\n')
567 {
568 *s = '\0';
569 s = buf;
570
571 hv_draw(buf, xx + x() - leftline_, yy + y());
572 if (underline) fl_xyline(xx + x() - leftline_, yy + y() + 1,
573 xx + x() - leftline_ +
574 (int)fl_width(buf));
575
576 current_pos = ptr-value_;
577 if (line < 31)
578 line ++;
579 xx = block->line[line];
580 yy += hh;
581 hh = fsize + 2;
582 }
583 else if (*ptr == '\t')
584 {
585 // Do tabs every 8 columns...
586 while (((s - buf) & 7))
587 *s++ = ' ';
588 }
589 else
590 *s++ = ' ';
591
592 if ((fsize + 2) > hh)
593 hh = fsize + 2;
594
595 ptr ++;
596 }
597
598 if (s > buf)
599 {
600 *s = '\0';
601 s = buf;
602
603 hv_draw(buf, xx + x() - leftline_, yy + y());
604 ww = (int)fl_width(buf);
605 if (underline) fl_xyline(xx + x() - leftline_, yy + y() + 1,
606 xx + x() - leftline_ + ww);
607 xx += ww;
608 current_pos = ptr-value_;
609 }
610
611 needspace = 0;
612 }
613 else
614 {
615 s = buf;
616
617 while (isspace((*ptr)&255))
618 ptr ++;
619 current_pos = ptr-value_;
620 }
621 }
622
623 if (*ptr == '<')
624 {
625 ptr ++;
626
627 if (strncmp(ptr, "!--", 3) == 0)
628 {
629 // Comment...
630 ptr += 3;
631 if ((ptr = strstr(ptr, "-->")) != NULL)
632 {
633 ptr += 3;
634 continue;
635 }
636 else
637 break;
638 }
639
640 while (*ptr && *ptr != '>' && !isspace((*ptr)&255))
641 if (s < (buf + sizeof(buf) - 1))
642 *s++ = *ptr++;
643 else
644 ptr ++;
645
646 *s = '\0';
647 s = buf;
648
649 attrs = ptr;
650 while (*ptr && *ptr != '>')
651 ptr ++;
652
653 if (*ptr == '>')
654 ptr ++;
655
656 // end of command reached, set the supposed start of printed eord here
657 current_pos = ptr-value_;
658 if (strcasecmp(buf, "HEAD") == 0)
659 head = 1;
660 else if (strcasecmp(buf, "BR") == 0)
661 {
662 if (line < 31)
663 line ++;
664 xx = block->line[line];
665 yy += hh;
666 hh = 0;
667 }
668 else if (strcasecmp(buf, "HR") == 0)
669 {
670 fl_line(block->x + x(), yy + y(), block->w + x(),
671 yy + y());
672
673 if (line < 31)
674 line ++;
675 xx = block->line[line];
676 yy += 2 * hh;
677 hh = 0;
678 }
679 else if (strcasecmp(buf, "CENTER") == 0 ||
680 strcasecmp(buf, "P") == 0 ||
681 strcasecmp(buf, "H1") == 0 ||
682 strcasecmp(buf, "H2") == 0 ||
683 strcasecmp(buf, "H3") == 0 ||
684 strcasecmp(buf, "H4") == 0 ||
685 strcasecmp(buf, "H5") == 0 ||
686 strcasecmp(buf, "H6") == 0 ||
687 strcasecmp(buf, "UL") == 0 ||
688 strcasecmp(buf, "OL") == 0 ||
689 strcasecmp(buf, "DL") == 0 ||
690 strcasecmp(buf, "LI") == 0 ||
691 strcasecmp(buf, "DD") == 0 ||
692 strcasecmp(buf, "DT") == 0 ||
693 strcasecmp(buf, "PRE") == 0)
694 {
695 if (tolower(buf[0]) == 'h')
696 {
697 font = FL_HELVETICA_BOLD;
698 fsize = textsize_ + '7' - buf[1];
699 }
700 else if (strcasecmp(buf, "DT") == 0)
701 {
702 font = textfont_ | FL_ITALIC;
703 fsize = textsize_;
704 }
705 else if (strcasecmp(buf, "PRE") == 0)
706 {
707 font = FL_COURIER;
708 fsize = textsize_;
709 pre = 1;
710 }
711
712 if (strcasecmp(buf, "LI") == 0)
713 {
714// fl_font(FL_SYMBOL, fsize); // The default SYMBOL font on my XP box is not Unicode...
715 char buf[8];
716 wchar_t b[] = {0x2022, 0x0};
717// buf[fl_unicode2utf(b, 1, buf)] = 0;
718 unsigned dstlen = fl_utf8fromwc(buf, 8, b, 1);
719 buf[dstlen] = 0;
720 hv_draw(buf, xx - fsize + x() - leftline_, yy + y());
721 }
722
723 pushfont(font, fsize);
724 }
725 else if (strcasecmp(buf, "A") == 0 &&
726 get_attr(attrs, "HREF", attr, sizeof(attr)) != NULL)
727 {
728 fl_color(linkcolor_);
729 underline = 1;
730 }
731 else if (strcasecmp(buf, "/A") == 0)
732 {
733 fl_color(textcolor_);
734 underline = 0;
735 }
736 else if (strcasecmp(buf, "FONT") == 0)
737 {
738 if (get_attr(attrs, "COLOR", attr, sizeof(attr)) != NULL) {
739 textcolor_ = get_color(attr, textcolor_);
740 }
741
742 if (get_attr(attrs, "FACE", attr, sizeof(attr)) != NULL) {
743 if (!strncasecmp(attr, "helvetica", 9) ||
744 !strncasecmp(attr, "arial", 5) ||
745 !strncasecmp(attr, "sans", 4)) font = FL_HELVETICA;
746 else if (!strncasecmp(attr, "times", 5) ||
747 !strncasecmp(attr, "serif", 5)) font = FL_TIMES;
748 else if (!strncasecmp(attr, "symbol", 6)) font = FL_SYMBOL;
749 else font = FL_COURIER;
750 }
751
752 if (get_attr(attrs, "SIZE", attr, sizeof(attr)) != NULL) {
753 if (isdigit(attr[0] & 255)) {
754 // Absolute size
755 fsize = (int)(textsize_ * pow(1.2, atof(attr) - 3.0));
756 } else {
757 // Relative size
758 fsize = (int)(fsize * pow(1.2, atof(attr) - 3.0));
759 }
760 }
761
762 pushfont(font, fsize);
763 }
764 else if (strcasecmp(buf, "/FONT") == 0)
765 {
766 popfont(font, fsize, textcolor_);
767 }
768 else if (strcasecmp(buf, "U") == 0)
769 underline = 1;
770 else if (strcasecmp(buf, "/U") == 0)
771 underline = 0;
772 else if (strcasecmp(buf, "B") == 0 ||
773 strcasecmp(buf, "STRONG") == 0)
774 pushfont(font |= FL_BOLD, fsize);
775 else if (strcasecmp(buf, "TD") == 0 ||
776 strcasecmp(buf, "TH") == 0)
777 {
778 int tx, ty, tw, th;
779
780 if (tolower(buf[1]) == 'h')
781 pushfont(font |= FL_BOLD, fsize);
782 else
783 pushfont(font = textfont_, fsize);
784
785 tx = block->x - 4 - leftline_;
786 ty = block->y - topline_ - fsize - 3;
787 tw = block->w - block->x + 7;
788 th = block->h + fsize - 5;
789
790 if (tx < 0)
791 {
792 tw += tx;
793 tx = 0;
794 }
795
796 if (ty < 0)
797 {
798 th += ty;
799 ty = 0;
800 }
801
802 tx += x();
803 ty += y();
804
805 if (block->bgcolor != bgcolor_)
806 {
807 fl_color(block->bgcolor);
808 fl_rectf(tx, ty, tw, th);
809 fl_color(textcolor_);
810 }
811
812 if (block->border)
813 fl_rect(tx, ty, tw, th);
814 }
815 else if (strcasecmp(buf, "I") == 0 ||
816 strcasecmp(buf, "EM") == 0)
817 pushfont(font |= FL_ITALIC, fsize);
818 else if (strcasecmp(buf, "CODE") == 0 ||
819 strcasecmp(buf, "TT") == 0)
820 pushfont(font = FL_COURIER, fsize);
821 else if (strcasecmp(buf, "KBD") == 0)
822 pushfont(font = FL_COURIER_BOLD, fsize);
823 else if (strcasecmp(buf, "VAR") == 0)
824 pushfont(font = FL_COURIER_ITALIC, fsize);
825 else if (strcasecmp(buf, "/HEAD") == 0)
826 head = 0;
827 else if (strcasecmp(buf, "/H1") == 0 ||
828 strcasecmp(buf, "/H2") == 0 ||
829 strcasecmp(buf, "/H3") == 0 ||
830 strcasecmp(buf, "/H4") == 0 ||
831 strcasecmp(buf, "/H5") == 0 ||
832 strcasecmp(buf, "/H6") == 0 ||
833 strcasecmp(buf, "/B") == 0 ||
834 strcasecmp(buf, "/STRONG") == 0 ||
835 strcasecmp(buf, "/I") == 0 ||
836 strcasecmp(buf, "/EM") == 0 ||
837 strcasecmp(buf, "/CODE") == 0 ||
838 strcasecmp(buf, "/TT") == 0 ||
839 strcasecmp(buf, "/KBD") == 0 ||
840 strcasecmp(buf, "/VAR") == 0)
841 popfont(font, fsize, fcolor);
842 else if (strcasecmp(buf, "/PRE") == 0)
843 {
844 popfont(font, fsize, fcolor);
845 pre = 0;
846 }
847 else if (strcasecmp(buf, "IMG") == 0)
848 {
849 Fl_Shared_Image *img = 0;
850 int width, height;
851 char wattr[8], hattr[8];
852
853
854 get_attr(attrs, "WIDTH", wattr, sizeof(wattr));
855 get_attr(attrs, "HEIGHT", hattr, sizeof(hattr));
856 width = get_length(wattr);
857 height = get_length(hattr);
858
859 if (get_attr(attrs, "SRC", attr, sizeof(attr))) {
860 img = get_image(attr, width, height);
861 if (!width) width = img->w();
862 if (!height) height = img->h();
863 }
864
865 if (!width || !height) {
866 if (get_attr(attrs, "ALT", attr, sizeof(attr)) == NULL) {
867 strcpy(attr, "IMG");
868 }
869 }
870
871 ww = width;
872
873 if (needspace && xx > block->x)
874 xx += (int)fl_width(' ');
875
876 if ((xx + ww) > block->w)
877 {
878 if (line < 31)
879 line ++;
880
881 xx = block->line[line];
882 yy += hh;
883 hh = 0;
884 }
885
886 if (img) {
887 img->draw(xx + x() - leftline_,
888 yy + y() - fl_height() + fl_descent() + 2);
889 }
890
891 xx += ww;
892 if ((height + 2) > hh)
893 hh = height + 2;
894
895 needspace = 0;
896 }
897 }
898 else if (*ptr == '\n' && pre)
899 {
900 *s = '\0';
901 s = buf;
902
903 hv_draw(buf, xx + x() - leftline_, yy + y());
904
905 if (line < 31)
906 line ++;
907 xx = block->line[line];
908 yy += hh;
909 hh = fsize + 2;
910 needspace = 0;
911
912 ptr ++;
913 current_pos = ptr-value_;
914 }
915 else if (isspace((*ptr)&255))
916 {
917 if (pre)
918 {
919 if (*ptr == ' ')
920 *s++ = ' ';
921 else
922 {
923 // Do tabs every 8 columns...
924 while (((s - buf) & 7))
925 *s++ = ' ';
926 }
927 }
928
929 ptr ++;
930 if (!pre) current_pos = ptr-value_;
931 needspace = 1;
932 }
933 else if (*ptr == '&')
934 {
935 ptr ++;
936
937 int qch = quote_char(ptr);
938
939 if (qch < 0)
940 *s++ = '&';
941 else {
942 int l;
943 l = fl_utf8encode((unsigned int) qch, s);
944 if (l < 1) l = 1;
945 s += l;
946 ptr = strchr(ptr, ';') + 1;
947 }
948
949 if ((fsize + 2) > hh)
950 hh = fsize + 2;
951 }
952 else
953 {
954 *s++ = *ptr++;
955
956 if ((fsize + 2) > hh)
957 hh = fsize + 2;
958 }
959 }
960
961 *s = '\0';
962
963 if (s > buf && !pre && !head)
964 {
965 ww = (int)fl_width(buf);
966
967 if (needspace && xx > block->x)
968 xx += (int)fl_width(' ');
969
970 if ((xx + ww) > block->w)
971 {
972 if (line < 31)
973 line ++;
974 xx = block->line[line];
975 yy += hh;
976 hh = 0;
977 }
978 }
979
980 if (s > buf && !head)
981 {
982 hv_draw(buf, xx + x() - leftline_, yy + y());
983 if (underline) fl_xyline(xx + x() - leftline_, yy + y() + 1,
984 xx + x() - leftline_ + ww);
985 current_pos = ptr-value_;
986 }
987 }
988
989 fl_pop_clip();
990}
991
992
993
994/** Finds the specified string \p s at starting position \p p.
995
996 \return the matching position or -1 if not found
997*/
998int // O - Matching position or -1 if not found
999Fl_Help_View::find(const char *s, // I - String to find
1000 int p) // I - Starting position
1001{
1002 int i, // Looping var
1003 c; // Current character
1004 Fl_Help_Block *b; // Current block
1005 const char *bp, // Block matching pointer
1006 *bs, // Start of current comparison
1007 *sp; // Search string pointer
1008
1009
1010 // Range check input and value...
1011 if (!s || !value_) return -1;
1012
1013 if (p < 0 || p >= (int)strlen(value_)) p = 0;
1014 else if (p > 0) p ++;
1015
1016 // Look for the string...
1017 for (i = nblocks_, b = blocks_; i > 0; i --, b ++) {
1018 if (b->end < (value_ + p))
1019 continue;
1020
1021 if (b->start < (value_ + p)) bp = value_ + p;
1022 else bp = b->start;
1023
1024 for (sp = s, bs = bp; *sp && *bp && bp < b->end; bp ++) {
1025 if (*bp == '<') {
1026 // skip to end of element...
1027 while (*bp && bp < b->end && *bp != '>') bp ++;
1028 continue;
1029 } else if (*bp == '&') {
1030 // decode HTML entity...
1031 if ((c = quote_char(bp + 1)) < 0) c = '&';
1032 else bp = strchr(bp + 1, ';') + 1;
1033 } else c = *bp;
1034
1035 if (tolower(*sp) == tolower(c)) sp ++;
1036 else {
1037 // No match, so reset to start of search...
1038 sp = s;
1039 bs ++;
1040 bp = bs;
1041 }
1042 }
1043
1044 if (!*sp) {
1045 // Found a match!
1046 topline(b->y - b->h);
1047 return (b->end - value_);
1048 }
1049 }
1050
1051 // No match!
1052 return (-1);
1053}
1054
1055/** Formats the help text. */
1056void Fl_Help_View::format() {
1057 int i; // Looping var
1058 int done; // Are we done yet?
1059 Fl_Help_Block *block, // Current block
1060 *cell; // Current table cell
1061 int cells[MAX_COLUMNS],
1062 // Cells in the current row...
1063 row; // Current table row (block number)
1064 const char *ptr, // Pointer into block
1065 *start, // Pointer to start of element
1066 *attrs; // Pointer to start of element attributes
1067 char *s, // Pointer into buffer
1068 buf[1024], // Text buffer
1069 attr[1024], // Attribute buffer
1070 wattr[1024], // Width attribute buffer
1071 hattr[1024], // Height attribute buffer
1072 linkdest[1024]; // Link destination
1073 int xx, yy, ww, hh; // Size of current text fragment
1074 int line; // Current line in block
1075 int links; // Links for current line
1076 Fl_Font font;
1077 Fl_Fontsize fsize; // Current font and size
1078 Fl_Color fcolor; // Current font color
1079 unsigned char border; // Draw border?
1080 int talign, // Current alignment
1081 newalign, // New alignment
1082 head, // In the <HEAD> section?
1083 pre, // <PRE> text?
1084 needspace; // Do we need whitespace?
1085 int table_width, // Width of table
1086 table_offset; // Offset of table
1087 int column, // Current table column number
1088 columns[MAX_COLUMNS];
1089 // Column widths
1090 Fl_Color tc, rc; // Table/row background color
1091 Fl_Boxtype b = box() ? box() : FL_DOWN_BOX;
1092 // Box to draw...
1093 fl_margins margins; // Left margin stack...
1094
1095
1096 // Reset document width...
1097 int scrollsize = scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size();
1098 hsize_ = w() - scrollsize - Fl::box_dw(b);
1099
1100 done = 0;
1101 while (!done)
1102 {
1103 // Reset state variables...
1104 done = 1;
1105 nblocks_ = 0;
1106 nlinks_ = 0;
1107 ntargets_ = 0;
1108 size_ = 0;
1109 bgcolor_ = color();
1110 textcolor_ = textcolor();
1111 linkcolor_ = fl_contrast(FL_BLUE, color());
1112
1113 tc = rc = bgcolor_;
1114
1115 strcpy(title_, "Untitled");
1116
1117 if (!value_)
1118 return;
1119
1120 // Setup for formatting...
1121 initfont(font, fsize, fcolor);
1122
1123 line = 0;
1124 links = 0;
1125 xx = margins.clear();
1126 yy = fsize + 2;
1127 ww = 0;
1128 column = 0;
1129 border = 0;
1130 hh = 0;
1131 block = add_block(value_, xx, yy, hsize_, 0);
1132 row = 0;
1133 head = 0;
1134 pre = 0;
1135 talign = LEFT;
1136 newalign = LEFT;
1137 needspace = 0;
1138 linkdest[0] = '\0';
1139 table_offset = 0;
1140
1141 // Html text character loop
1142 for (ptr = value_, s = buf; *ptr;)
1143 {
1144 // End of word?
1145 if ((*ptr == '<' || isspace((*ptr)&255)) && s > buf)
1146 {
1147 // Get width of word parsed so far...
1148 *s = '\0';
1149 ww = (int)fl_width(buf);
1150
1151 if (!head && !pre)
1152 {
1153 // Check width...
1154 if (ww > hsize_) {
1155 hsize_ = ww;
1156 done = 0;
1157 break;
1158 }
1159
1160 if (needspace && xx > block->x)
1161 ww += (int)fl_width(' ');
1162
1163 // printf("line = %d, xx = %d, ww = %d, block->x = %d, block->w = %d\n",
1164 // line, xx, ww, block->x, block->w);
1165
1166 if ((xx + ww) > block->w)
1167 {
1168 line = do_align(block, line, xx, newalign, links);
1169 xx = block->x;
1170 yy += hh;
1171 block->h += hh;
1172 hh = 0;
1173 }
1174
1175 if (linkdest[0])
1176 add_link(linkdest, xx, yy - fsize, ww, fsize);
1177
1178 xx += ww;
1179 if ((fsize + 2) > hh)
1180 hh = fsize + 2;
1181
1182 needspace = 0;
1183 }
1184 else if (pre)
1185 {
1186 // Add a link as needed...
1187 if (linkdest[0])
1188 add_link(linkdest, xx, yy - hh, ww, hh);
1189
1190 xx += ww;
1191 if ((fsize + 2) > hh)
1192 hh = fsize + 2;
1193
1194 // Handle preformatted text...
1195 while (isspace((*ptr)&255))
1196 {
1197 if (*ptr == '\n')
1198 {
1199 if (xx > hsize_) break;
1200
1201 line = do_align(block, line, xx, newalign, links);
1202 xx = block->x;
1203 yy += hh;
1204 block->h += hh;
1205 hh = fsize + 2;
1206 }
1207 else
1208 xx += (int)fl_width(' ');
1209
1210 if ((fsize + 2) > hh)
1211 hh = fsize + 2;
1212
1213 ptr ++;
1214 }
1215
1216 if (xx > hsize_) {
1217 hsize_ = xx;
1218 done = 0;
1219 break;
1220 }
1221
1222 needspace = 0;
1223 }
1224 else
1225 {
1226 // Handle normal text or stuff in the <HEAD> section...
1227 while (isspace((*ptr)&255))
1228 ptr ++;
1229 }
1230
1231 s = buf;
1232 }
1233
1234 if (*ptr == '<')
1235 {
1236 // Handle html tags..
1237 start = ptr;
1238 ptr ++;
1239
1240 if (strncmp(ptr, "!--", 3) == 0)
1241 {
1242 // Comment...
1243 ptr += 3;
1244 if ((ptr = strstr(ptr, "-->")) != NULL)
1245 {
1246 ptr += 3;
1247 continue;
1248 }
1249 else
1250 break;
1251 }
1252
1253 while (*ptr && *ptr != '>' && !isspace((*ptr)&255))
1254 if (s < (buf + sizeof(buf) - 1))
1255 *s++ = *ptr++;
1256 else
1257 ptr ++;
1258
1259 *s = '\0';
1260 s = buf;
1261
1262// puts(buf);
1263
1264 attrs = ptr;
1265 while (*ptr && *ptr != '>')
1266 ptr ++;
1267
1268 if (*ptr == '>')
1269 ptr ++;
1270
1271 if (strcasecmp(buf, "HEAD") == 0)
1272 head = 1;
1273 else if (strcasecmp(buf, "/HEAD") == 0)
1274 head = 0;
1275 else if (strcasecmp(buf, "TITLE") == 0)
1276 {
1277 // Copy the title in the document...
1278 for (s = title_;
1279 *ptr != '<' && *ptr && s < (title_ + sizeof(title_) - 1);
1280 *s++ = *ptr++);
1281
1282 *s = '\0';
1283 s = buf;
1284 }
1285 else if (strcasecmp(buf, "A") == 0)
1286 {
1287 if (get_attr(attrs, "NAME", attr, sizeof(attr)) != NULL)
1288 add_target(attr, yy - fsize - 2);
1289
1290 if (get_attr(attrs, "HREF", attr, sizeof(attr)) != NULL)
1291 strlcpy(linkdest, attr, sizeof(linkdest));
1292 }
1293 else if (strcasecmp(buf, "/A") == 0)
1294 linkdest[0] = '\0';
1295 else if (strcasecmp(buf, "BODY") == 0)
1296 {
1297 bgcolor_ = get_color(get_attr(attrs, "BGCOLOR", attr, sizeof(attr)),
1298 color());
1299 textcolor_ = get_color(get_attr(attrs, "TEXT", attr, sizeof(attr)),
1300 textcolor());
1301 linkcolor_ = get_color(get_attr(attrs, "LINK", attr, sizeof(attr)),
1302 fl_contrast(FL_BLUE, color()));
1303 }
1304 else if (strcasecmp(buf, "BR") == 0)
1305 {
1306 line = do_align(block, line, xx, newalign, links);
1307 xx = block->x;
1308 block->h += hh;
1309 yy += hh;
1310 hh = 0;
1311 }
1312 else if (strcasecmp(buf, "CENTER") == 0 ||
1313 strcasecmp(buf, "P") == 0 ||
1314 strcasecmp(buf, "H1") == 0 ||
1315 strcasecmp(buf, "H2") == 0 ||
1316 strcasecmp(buf, "H3") == 0 ||
1317 strcasecmp(buf, "H4") == 0 ||
1318 strcasecmp(buf, "H5") == 0 ||
1319 strcasecmp(buf, "H6") == 0 ||
1320 strcasecmp(buf, "UL") == 0 ||
1321 strcasecmp(buf, "OL") == 0 ||
1322 strcasecmp(buf, "DL") == 0 ||
1323 strcasecmp(buf, "LI") == 0 ||
1324 strcasecmp(buf, "DD") == 0 ||
1325 strcasecmp(buf, "DT") == 0 ||
1326 strcasecmp(buf, "HR") == 0 ||
1327 strcasecmp(buf, "PRE") == 0 ||
1328 strcasecmp(buf, "TABLE") == 0)
1329 {
1330 block->end = start;
1331 line = do_align(block, line, xx, newalign, links);
1332 newalign = strcasecmp(buf, "CENTER") ? LEFT : CENTER;
1333 xx = block->x;
1334 block->h += hh;
1335
1336 if (strcasecmp(buf, "UL") == 0 ||
1337 strcasecmp(buf, "OL") == 0 ||
1338 strcasecmp(buf, "DL") == 0)
1339 {
1340 block->h += fsize + 2;
1341 xx = margins.push(4 * fsize);
1342 }
1343 else if (strcasecmp(buf, "TABLE") == 0)
1344 {
1345 if (get_attr(attrs, "BORDER", attr, sizeof(attr)))
1346 border = (uchar)atoi(attr);
1347 else
1348 border = 0;
1349
1350 tc = rc = get_color(get_attr(attrs, "BGCOLOR", attr, sizeof(attr)), bgcolor_);
1351
1352 block->h += fsize + 2;
1353
1354 format_table(&table_width, columns, start);
1355
1356 if ((xx + table_width) > hsize_) {
1357#ifdef DEBUG
1358 printf("xx=%d, table_width=%d, hsize_=%d\n", xx, table_width,
1359 hsize_);
1360#endif // DEBUG
1361 hsize_ = xx + table_width;
1362 done = 0;
1363 break;
1364 }
1365
1366 switch (get_align(attrs, talign))
1367 {
1368 default :
1369 table_offset = 0;
1370 break;
1371
1372 case CENTER :
1373 table_offset = (hsize_ - table_width) / 2 - textsize_;
1374 break;
1375
1376 case RIGHT :
1377 table_offset = hsize_ - table_width - textsize_;
1378 break;
1379 }
1380
1381 column = 0;
1382 }
1383
1384 if (tolower(buf[0]) == 'h' && isdigit(buf[1]))
1385 {
1386 font = FL_HELVETICA_BOLD;
1387 fsize = textsize_ + '7' - buf[1];
1388 }
1389 else if (strcasecmp(buf, "DT") == 0)
1390 {
1391 font = textfont_ | FL_ITALIC;
1392 fsize = textsize_;
1393 }
1394 else if (strcasecmp(buf, "PRE") == 0)
1395 {
1396 font = FL_COURIER;
1397 fsize = textsize_;
1398 pre = 1;
1399 }
1400 else
1401 {
1402 font = textfont_;
1403 fsize = textsize_;
1404 }
1405
1406 pushfont(font, fsize);
1407
1408 yy = block->y + block->h;
1409 hh = 0;
1410
1411 if ((tolower(buf[0]) == 'h' && isdigit(buf[1])) ||
1412 strcasecmp(buf, "DD") == 0 ||
1413 strcasecmp(buf, "DT") == 0 ||
1414 strcasecmp(buf, "P") == 0)
1415 yy += fsize + 2;
1416 else if (strcasecmp(buf, "HR") == 0)
1417 {
1418 hh += 2 * fsize;
1419 yy += fsize;
1420 }
1421
1422 if (row)
1423 block = add_block(start, xx, yy, block->w, 0);
1424 else
1425 block = add_block(start, xx, yy, hsize_, 0);
1426
1427 needspace = 0;
1428 line = 0;
1429
1430 if (strcasecmp(buf, "CENTER") == 0)
1431 newalign = talign = CENTER;
1432 else
1433 newalign = get_align(attrs, talign);
1434 }
1435 else if (strcasecmp(buf, "/CENTER") == 0 ||
1436 strcasecmp(buf, "/P") == 0 ||
1437 strcasecmp(buf, "/H1") == 0 ||
1438 strcasecmp(buf, "/H2") == 0 ||
1439 strcasecmp(buf, "/H3") == 0 ||
1440 strcasecmp(buf, "/H4") == 0 ||
1441 strcasecmp(buf, "/H5") == 0 ||
1442 strcasecmp(buf, "/H6") == 0 ||
1443 strcasecmp(buf, "/PRE") == 0 ||
1444 strcasecmp(buf, "/UL") == 0 ||
1445 strcasecmp(buf, "/OL") == 0 ||
1446 strcasecmp(buf, "/DL") == 0 ||
1447 strcasecmp(buf, "/TABLE") == 0)
1448 {
1449 line = do_align(block, line, xx, newalign, links);
1450 xx = block->x;
1451 block->end = ptr;
1452
1453 if (strcasecmp(buf, "/UL") == 0 ||
1454 strcasecmp(buf, "/OL") == 0 ||
1455 strcasecmp(buf, "/DL") == 0)
1456 {
1457 xx = margins.pop();
1458 block->h += fsize + 2;
1459 }
1460 else if (strcasecmp(buf, "/TABLE") == 0)
1461 {
1462 block->h += fsize + 2;
1463 xx = margins.current();
1464 }
1465 else if (strcasecmp(buf, "/PRE") == 0)
1466 {
1467 pre = 0;
1468 hh = 0;
1469 }
1470 else if (strcasecmp(buf, "/CENTER") == 0)
1471 talign = LEFT;
1472
1473 popfont(font, fsize, fcolor);
1474
1475 //#if defined(__GNUC__)
1476 //#warning FIXME this isspace & 255 test will probably not work on a utf8 stream... And we use it everywhere!
1477 //#endif /*__GNUC__*/
1478 while (isspace((*ptr)&255))
1479 ptr ++;
1480
1481 block->h += hh;
1482 yy += hh;
1483
1484 if (tolower(buf[2]) == 'l')
1485 yy += fsize + 2;
1486
1487 if (row)
1488 block = add_block(ptr, xx, yy, block->w, 0);
1489 else
1490 block = add_block(ptr, xx, yy, hsize_, 0);
1491
1492 needspace = 0;
1493 hh = 0;
1494 line = 0;
1495 newalign = talign;
1496 }
1497 else if (strcasecmp(buf, "TR") == 0)
1498 {
1499 block->end = start;
1500 line = do_align(block, line, xx, newalign, links);
1501 xx = block->x;
1502 block->h += hh;
1503
1504 if (row)
1505 {
1506 yy = blocks_[row].y + blocks_[row].h;
1507
1508 for (cell = blocks_ + row + 1; cell <= block; cell ++)
1509 if ((cell->y + cell->h) > yy)
1510 yy = cell->y + cell->h;
1511
1512 block = blocks_ + row;
1513
1514 block->h = yy - block->y + 2;
1515
1516 for (i = 0; i < column; i ++)
1517 if (cells[i])
1518 {
1519 cell = blocks_ + cells[i];
1520 cell->h = block->h;
1521 }
1522 }
1523
1524 memset(cells, 0, sizeof(cells));
1525
1526 yy = block->y + block->h - 4;
1527 hh = 0;
1528 block = add_block(start, xx, yy, hsize_, 0);
1529 row = block - blocks_;
1530 needspace = 0;
1531 column = 0;
1532 line = 0;
1533
1534 rc = get_color(get_attr(attrs, "BGCOLOR", attr, sizeof(attr)), tc);
1535 }
1536 else if (strcasecmp(buf, "/TR") == 0 && row)
1537 {
1538 line = do_align(block, line, xx, newalign, links);
1539 block->end = start;
1540 block->h += hh;
1541 talign = LEFT;
1542
1543 xx = blocks_[row].x;
1544 yy = blocks_[row].y + blocks_[row].h;
1545
1546 for (cell = blocks_ + row + 1; cell <= block; cell ++)
1547 if ((cell->y + cell->h) > yy)
1548 yy = cell->y + cell->h;
1549
1550 block = blocks_ + row;
1551
1552 block->h = yy - block->y + 2;
1553
1554 for (i = 0; i < column; i ++)
1555 if (cells[i])
1556 {
1557 cell = blocks_ + cells[i];
1558 cell->h = block->h;
1559 }
1560
1561 yy = block->y + block->h /*- 4*/;
1562 block = add_block(start, xx, yy, hsize_, 0);
1563 needspace = 0;
1564 row = 0;
1565 line = 0;
1566 }
1567 else if ((strcasecmp(buf, "TD") == 0 ||
1568 strcasecmp(buf, "TH") == 0) && row)
1569 {
1570 int colspan; // COLSPAN attribute
1571
1572
1573 line = do_align(block, line, xx, newalign, links);
1574 block->end = start;
1575 block->h += hh;
1576
1577 if (strcasecmp(buf, "TH") == 0)
1578 font = textfont_ | FL_BOLD;
1579 else
1580 font = textfont_;
1581
1582 fsize = textsize_;
1583
1584 xx = blocks_[row].x + fsize + 3 + table_offset;
1585 for (i = 0; i < column; i ++)
1586 xx += columns[i] + 6;
1587
1588 margins.push(xx - margins.current());
1589
1590 if (get_attr(attrs, "COLSPAN", attr, sizeof(attr)) != NULL)
1591 colspan = atoi(attr);
1592 else
1593 colspan = 1;
1594
1595 for (i = 0, ww = -6; i < colspan; i ++)
1596 ww += columns[column + i] + 6;
1597
1598 if (block->end == block->start && nblocks_ > 1)
1599 {
1600 nblocks_ --;
1601 block --;
1602 }
1603
1604 pushfont(font, fsize);
1605
1606 yy = blocks_[row].y;
1607 hh = 0;
1608 block = add_block(start, xx, yy, xx + ww, 0, border);
1609 needspace = 0;
1610 line = 0;
1611 newalign = get_align(attrs, tolower(buf[1]) == 'h' ? CENTER : LEFT);
1612 talign = newalign;
1613
1614 cells[column] = block - blocks_;
1615
1616 column += colspan;
1617
1618 block->bgcolor = get_color(get_attr(attrs, "BGCOLOR", attr,
1619 sizeof(attr)), rc);
1620 }
1621 else if ((strcasecmp(buf, "/TD") == 0 ||
1622 strcasecmp(buf, "/TH") == 0) && row)
1623 {
1624 line = do_align(block, line, xx, newalign, links);
1625 popfont(font, fsize, fcolor);
1626 xx = margins.pop();
1627 talign = LEFT;
1628 }
1629 else if (strcasecmp(buf, "FONT") == 0)
1630 {
1631 if (get_attr(attrs, "FACE", attr, sizeof(attr)) != NULL) {
1632 if (!strncasecmp(attr, "helvetica", 9) ||
1633 !strncasecmp(attr, "arial", 5) ||
1634 !strncasecmp(attr, "sans", 4)) font = FL_HELVETICA;
1635 else if (!strncasecmp(attr, "times", 5) ||
1636 !strncasecmp(attr, "serif", 5)) font = FL_TIMES;
1637 else if (!strncasecmp(attr, "symbol", 6)) font = FL_SYMBOL;
1638 else font = FL_COURIER;
1639 }
1640
1641 if (get_attr(attrs, "SIZE", attr, sizeof(attr)) != NULL) {
1642 if (isdigit(attr[0] & 255)) {
1643 // Absolute size
1644 fsize = (int)(textsize_ * pow(1.2, atoi(attr) - 3.0));
1645 } else {
1646 // Relative size
1647 fsize = (int)(fsize * pow(1.2, atoi(attr)));
1648 }
1649 }
1650
1651 pushfont(font, fsize);
1652 }
1653 else if (strcasecmp(buf, "/FONT") == 0)
1654 popfont(font, fsize, fcolor);
1655 else if (strcasecmp(buf, "B") == 0 ||
1656 strcasecmp(buf, "STRONG") == 0)
1657 pushfont(font |= FL_BOLD, fsize);
1658 else if (strcasecmp(buf, "I") == 0 ||
1659 strcasecmp(buf, "EM") == 0)
1660 pushfont(font |= FL_ITALIC, fsize);
1661 else if (strcasecmp(buf, "CODE") == 0 ||
1662 strcasecmp(buf, "TT") == 0)
1663 pushfont(font = FL_COURIER, fsize);
1664 else if (strcasecmp(buf, "KBD") == 0)
1665 pushfont(font = FL_COURIER_BOLD, fsize);
1666 else if (strcasecmp(buf, "VAR") == 0)
1667 pushfont(font = FL_COURIER_ITALIC, fsize);
1668 else if (strcasecmp(buf, "/B") == 0 ||
1669 strcasecmp(buf, "/STRONG") == 0 ||
1670 strcasecmp(buf, "/I") == 0 ||
1671 strcasecmp(buf, "/EM") == 0 ||
1672 strcasecmp(buf, "/CODE") == 0 ||
1673 strcasecmp(buf, "/TT") == 0 ||
1674 strcasecmp(buf, "/KBD") == 0 ||
1675 strcasecmp(buf, "/VAR") == 0)
1676 popfont(font, fsize, fcolor);
1677 else if (strcasecmp(buf, "IMG") == 0)
1678 {
1679 Fl_Shared_Image *img = 0;
1680 int width;
1681 int height;
1682
1683
1684 get_attr(attrs, "WIDTH", wattr, sizeof(wattr));
1685 get_attr(attrs, "HEIGHT", hattr, sizeof(hattr));
1686 width = get_length(wattr);
1687 height = get_length(hattr);
1688
1689 if (get_attr(attrs, "SRC", attr, sizeof(attr))) {
1690 img = get_image(attr, width, height);
1691 width = img->w();
1692 height = img->h();
1693 }
1694
1695 ww = width;
1696
1697 if (ww > hsize_) {
1698 hsize_ = ww;
1699 done = 0;
1700 break;
1701 }
1702
1703 if (needspace && xx > block->x)
1704 ww += (int)fl_width(' ');
1705
1706 if ((xx + ww) > block->w)
1707 {
1708 line = do_align(block, line, xx, newalign, links);
1709 xx = block->x;
1710 yy += hh;
1711 block->h += hh;
1712 hh = 0;
1713 }
1714
1715 if (linkdest[0])
1716 add_link(linkdest, xx, yy - height, ww, height);
1717
1718 xx += ww;
1719 if ((height + 2) > hh)
1720 hh = height + 2;
1721
1722 needspace = 0;
1723 }
1724 }
1725 else if (*ptr == '\n' && pre)
1726 {
1727 if (linkdest[0])
1728 add_link(linkdest, xx, yy - hh, ww, hh);
1729
1730 if (xx > hsize_) {
1731 hsize_ = xx;
1732 done = 0;
1733 break;
1734 }
1735
1736 line = do_align(block, line, xx, newalign, links);
1737 xx = block->x;
1738 yy += hh;
1739 block->h += hh;
1740 needspace = 0;
1741 ptr ++;
1742 }
1743 else if (isspace((*ptr)&255))
1744 {
1745 needspace = 1;
1746 if ( pre ) {
1747 xx += (int)fl_width(' ');
1748 }
1749 ptr ++;
1750 }
1751 else if (*ptr == '&' && s < (buf + sizeof(buf) - 1))
1752 {
1753 // Handle html '&' codes, eg. "&amp;"
1754 ptr ++;
1755
1756 int qch = quote_char(ptr);
1757
1758 if (qch < 0)
1759 *s++ = '&';
1760 else {
1761 int l;
1762 l = fl_utf8encode((unsigned int) qch, s);
1763 if (l < 1) l = 1;
1764 s += l;
1765 ptr = strchr(ptr, ';') + 1;
1766 }
1767
1768 if ((fsize + 2) > hh)
1769 hh = fsize + 2;
1770 }
1771 else
1772 {
1773 if (s < (buf + sizeof(buf) - 1))
1774 *s++ = *ptr++;
1775 else
1776 ptr ++;
1777
1778 if ((fsize + 2) > hh)
1779 hh = fsize + 2;
1780 }
1781 }
1782
1783 if (s > buf && !head)
1784 {
1785 *s = '\0';
1786 ww = (int)fl_width(buf);
1787
1788 // printf("line = %d, xx = %d, ww = %d, block->x = %d, block->w = %d\n",
1789 // line, xx, ww, block->x, block->w);
1790
1791 if (ww > hsize_) {
1792 hsize_ = ww;
1793 done = 0;
1794 break;
1795 }
1796
1797 if (needspace && xx > block->x)
1798 ww += (int)fl_width(' ');
1799
1800 if ((xx + ww) > block->w)
1801 {
1802 line = do_align(block, line, xx, newalign, links);
1803 xx = block->x;
1804 yy += hh;
1805 block->h += hh;
1806 hh = 0;
1807 }
1808
1809 if (linkdest[0])
1810 add_link(linkdest, xx, yy - fsize, ww, fsize);
1811
1812 xx += ww;
1813 }
1814
1815 do_align(block, line, xx, newalign, links);
1816
1817 block->end = ptr;
1818 size_ = yy + hh;
1819 }
1820
1821// printf("margins.depth_=%d\n", margins.depth_);
1822
1823 if (ntargets_ > 1)
1824 qsort(targets_, ntargets_, sizeof(Fl_Help_Target),
1825 (compare_func_t)compare_targets);
1826
1827 int dx = Fl::box_dw(b) - Fl::box_dx(b);
1828 int dy = Fl::box_dh(b) - Fl::box_dy(b);
1829 int ss = scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size();
1830 int dw = Fl::box_dw(b) + ss;
1831 int dh = Fl::box_dh(b);
1832
1833 if (hsize_ > (w() - dw)) {
1834 hscrollbar_.show();
1835
1836 dh += ss;
1837
1838 if (size_ < (h() - dh)) {
1839 scrollbar_.hide();
1840 hscrollbar_.resize(x() + Fl::box_dx(b), y() + h() - ss - dy,
1841 w() - Fl::box_dw(b), ss);
1842 } else {
1843 scrollbar_.show();
1844 scrollbar_.resize(x() + w() - ss - dx, y() + Fl::box_dy(b),
1845 ss, h() - ss - Fl::box_dh(b));
1846 hscrollbar_.resize(x() + Fl::box_dx(b), y() + h() - ss - dy,
1847 w() - ss - Fl::box_dw(b), ss);
1848 }
1849 } else {
1850 hscrollbar_.hide();
1851
1852 if (size_ < (h() - dh)) scrollbar_.hide();
1853 else {
1854 scrollbar_.resize(x() + w() - ss - dx, y() + Fl::box_dy(b),
1855 ss, h() - Fl::box_dh(b));
1856 scrollbar_.show();
1857 }
1858 }
1859
1860 // Reset scrolling if it needs to be...
1861 if (scrollbar_.visible()) {
1862 int temph = h() - Fl::box_dh(b);
1863 if (hscrollbar_.visible()) temph -= ss;
1864 if ((topline_ + temph) > size_) topline(size_ - temph);
1865 else topline(topline_);
1866 } else topline(0);
1867
1868 if (hscrollbar_.visible()) {
1869 int tempw = w() - ss - Fl::box_dw(b);
1870 if ((leftline_ + tempw) > hsize_) leftline(hsize_ - tempw);
1871 else leftline(leftline_);
1872 } else leftline(0);
1873}
1874
1875
1876/** Formats a table */
1877void
1878Fl_Help_View::format_table(int *table_width, // O - Total table width
1879 int *columns, // O - Column widths
1880 const char *table) // I - Pointer to start of table
1881{
1882 int column, // Current column
1883 num_columns, // Number of columns
1884 colspan, // COLSPAN attribute
1885 width, // Current width
1886 temp_width, // Temporary width
1887 max_width, // Maximum width
1888 incell, // In a table cell?
1889 pre, // <PRE> text?
1890 needspace; // Need whitespace?
1891 char *s, // Pointer into buffer
1892 buf[1024], // Text buffer
1893 attr[1024], // Other attribute
1894 wattr[1024], // WIDTH attribute
1895 hattr[1024]; // HEIGHT attribute
1896 const char *ptr, // Pointer into table
1897 *attrs, // Pointer to attributes
1898 *start; // Start of element
1899 int minwidths[MAX_COLUMNS]; // Minimum widths for each column
1900 Fl_Font font;
1901 Fl_Fontsize fsize; // Current font and size
1902 Fl_Color fcolor; // Currrent font color
1903
1904 // Clear widths...
1905 *table_width = 0;
1906 for (column = 0; column < MAX_COLUMNS; column ++)
1907 {
1908 columns[column] = 0;
1909 minwidths[column] = 0;
1910 }
1911
1912 num_columns = 0;
1913 colspan = 0;
1914 max_width = 0;
1915 pre = 0;
1916 needspace = 0;
1917 fstack_.top(font, fsize, fcolor);
1918
1919 // Scan the table...
1920 for (ptr = table, column = -1, width = 0, s = buf, incell = 0; *ptr;)
1921 {
1922 if ((*ptr == '<' || isspace((*ptr)&255)) && s > buf && incell)
1923 {
1924 // Check width...
1925 if (needspace)
1926 {
1927 *s++ = ' ';
1928 needspace = 0;
1929 }
1930
1931 *s = '\0';
1932 temp_width = (int)fl_width(buf);
1933 s = buf;
1934
1935 if (temp_width > minwidths[column])
1936 minwidths[column] = temp_width;
1937
1938 width += temp_width;
1939
1940 if (width > max_width)
1941 max_width = width;
1942 }
1943
1944 if (*ptr == '<')
1945 {
1946 start = ptr;
1947
1948 for (s = buf, ptr ++; *ptr && *ptr != '>' && !isspace((*ptr)&255);)
1949 if (s < (buf + sizeof(buf) - 1))
1950 *s++ = *ptr++;
1951 else
1952 ptr ++;
1953
1954 *s = '\0';
1955 s = buf;
1956
1957 attrs = ptr;
1958 while (*ptr && *ptr != '>')
1959 ptr ++;
1960
1961 if (*ptr == '>')
1962 ptr ++;
1963
1964 if (strcasecmp(buf, "BR") == 0 ||
1965 strcasecmp(buf, "HR") == 0)
1966 {
1967 width = 0;
1968 needspace = 0;
1969 }
1970 else if (strcasecmp(buf, "TABLE") == 0 && start > table)
1971 break;
1972 else if (strcasecmp(buf, "CENTER") == 0 ||
1973 strcasecmp(buf, "P") == 0 ||
1974 strcasecmp(buf, "H1") == 0 ||
1975 strcasecmp(buf, "H2") == 0 ||
1976 strcasecmp(buf, "H3") == 0 ||
1977 strcasecmp(buf, "H4") == 0 ||
1978 strcasecmp(buf, "H5") == 0 ||
1979 strcasecmp(buf, "H6") == 0 ||
1980 strcasecmp(buf, "UL") == 0 ||
1981 strcasecmp(buf, "OL") == 0 ||
1982 strcasecmp(buf, "DL") == 0 ||
1983 strcasecmp(buf, "LI") == 0 ||
1984 strcasecmp(buf, "DD") == 0 ||
1985 strcasecmp(buf, "DT") == 0 ||
1986 strcasecmp(buf, "PRE") == 0)
1987 {
1988 width = 0;
1989 needspace = 0;
1990
1991 if (tolower(buf[0]) == 'h' && isdigit(buf[1]))
1992 {
1993 font = FL_HELVETICA_BOLD;
1994 fsize = textsize_ + '7' - buf[1];
1995 }
1996 else if (strcasecmp(buf, "DT") == 0)
1997 {
1998 font = textfont_ | FL_ITALIC;
1999 fsize = textsize_;
2000 }
2001 else if (strcasecmp(buf, "PRE") == 0)
2002 {
2003 font = FL_COURIER;
2004 fsize = textsize_;
2005 pre = 1;
2006 }
2007 else if (strcasecmp(buf, "LI") == 0)
2008 {
2009 width += 4 * fsize;
2010 font = textfont_;
2011 fsize = textsize_;
2012 }
2013 else
2014 {
2015 font = textfont_;
2016 fsize = textsize_;
2017 }
2018
2019 pushfont(font, fsize);
2020 }
2021 else if (strcasecmp(buf, "/CENTER") == 0 ||
2022 strcasecmp(buf, "/P") == 0 ||
2023 strcasecmp(buf, "/H1") == 0 ||
2024 strcasecmp(buf, "/H2") == 0 ||
2025 strcasecmp(buf, "/H3") == 0 ||
2026 strcasecmp(buf, "/H4") == 0 ||
2027 strcasecmp(buf, "/H5") == 0 ||
2028 strcasecmp(buf, "/H6") == 0 ||
2029 strcasecmp(buf, "/PRE") == 0 ||
2030 strcasecmp(buf, "/UL") == 0 ||
2031 strcasecmp(buf, "/OL") == 0 ||
2032 strcasecmp(buf, "/DL") == 0)
2033 {
2034 width = 0;
2035 needspace = 0;
2036
2037 popfont(font, fsize, fcolor);
2038 }
2039 else if (strcasecmp(buf, "TR") == 0 || strcasecmp(buf, "/TR") == 0 ||
2040 strcasecmp(buf, "/TABLE") == 0)
2041 {
2042// printf("%s column = %d, colspan = %d, num_columns = %d\n",
2043// buf, column, colspan, num_columns);
2044
2045 if (column >= 0)
2046 {
2047 // This is a hack to support COLSPAN...
2048 max_width /= colspan;
2049
2050 while (colspan > 0)
2051 {
2052 if (max_width > columns[column])
2053 columns[column] = max_width;
2054
2055 column ++;
2056 colspan --;
2057 }
2058 }
2059
2060 if (strcasecmp(buf, "/TABLE") == 0)
2061 break;
2062
2063 needspace = 0;
2064 column = -1;
2065 width = 0;
2066 max_width = 0;
2067 incell = 0;
2068 }
2069 else if (strcasecmp(buf, "TD") == 0 ||
2070 strcasecmp(buf, "TH") == 0)
2071 {
2072// printf("BEFORE column = %d, colspan = %d, num_columns = %d\n",
2073// column, colspan, num_columns);
2074
2075 if (column >= 0)
2076 {
2077 // This is a hack to support COLSPAN...
2078 max_width /= colspan;
2079
2080 while (colspan > 0)
2081 {
2082 if (max_width > columns[column])
2083 columns[column] = max_width;
2084
2085 column ++;
2086 colspan --;
2087 }
2088 }
2089 else
2090 column ++;
2091
2092 if (get_attr(attrs, "COLSPAN", attr, sizeof(attr)) != NULL)
2093 colspan = atoi(attr);
2094 else
2095 colspan = 1;
2096
2097// printf("AFTER column = %d, colspan = %d, num_columns = %d\n",
2098// column, colspan, num_columns);
2099
2100 if ((column + colspan) >= num_columns)
2101 num_columns = column + colspan;
2102
2103 needspace = 0;
2104 width = 0;
2105 incell = 1;
2106
2107 if (strcasecmp(buf, "TH") == 0)
2108 font = textfont_ | FL_BOLD;
2109 else
2110 font = textfont_;
2111
2112 fsize = textsize_;
2113
2114 pushfont(font, fsize);
2115
2116 if (get_attr(attrs, "WIDTH", attr, sizeof(attr)) != NULL)
2117 max_width = get_length(attr);
2118 else
2119 max_width = 0;
2120
2121// printf("max_width = %d\n", max_width);
2122 }
2123 else if (strcasecmp(buf, "/TD") == 0 ||
2124 strcasecmp(buf, "/TH") == 0)
2125 {
2126 incell = 0;
2127 popfont(font, fsize, fcolor);
2128 }
2129 else if (strcasecmp(buf, "B") == 0 ||
2130 strcasecmp(buf, "STRONG") == 0)
2131 pushfont(font |= FL_BOLD, fsize);
2132 else if (strcasecmp(buf, "I") == 0 ||
2133 strcasecmp(buf, "EM") == 0)
2134 pushfont(font |= FL_ITALIC, fsize);
2135 else if (strcasecmp(buf, "CODE") == 0 ||
2136 strcasecmp(buf, "TT") == 0)
2137 pushfont(font = FL_COURIER, fsize);
2138 else if (strcasecmp(buf, "KBD") == 0)
2139 pushfont(font = FL_COURIER_BOLD, fsize);
2140 else if (strcasecmp(buf, "VAR") == 0)
2141 pushfont(font = FL_COURIER_ITALIC, fsize);
2142 else if (strcasecmp(buf, "/B") == 0 ||
2143 strcasecmp(buf, "/STRONG") == 0 ||
2144 strcasecmp(buf, "/I") == 0 ||
2145 strcasecmp(buf, "/EM") == 0 ||
2146 strcasecmp(buf, "/CODE") == 0 ||
2147 strcasecmp(buf, "/TT") == 0 ||
2148 strcasecmp(buf, "/KBD") == 0 ||
2149 strcasecmp(buf, "/VAR") == 0)
2150 popfont(font, fsize, fcolor);
2151 else if (strcasecmp(buf, "IMG") == 0 && incell)
2152 {
2153 Fl_Shared_Image *img = 0;
2154 int iwidth, iheight;
2155
2156
2157 get_attr(attrs, "WIDTH", wattr, sizeof(wattr));
2158 get_attr(attrs, "HEIGHT", hattr, sizeof(hattr));
2159 iwidth = get_length(wattr);
2160 iheight = get_length(hattr);
2161
2162 if (get_attr(attrs, "SRC", attr, sizeof(attr))) {
2163 img = get_image(attr, iwidth, iheight);
2164 iwidth = img->w();
2165 iheight = img->h();
2166 }
2167
2168 if (iwidth > minwidths[column])
2169 minwidths[column] = iwidth;
2170
2171 width += iwidth;
2172 if (needspace)
2173 width += (int)fl_width(' ');
2174
2175 if (width > max_width)
2176 max_width = width;
2177
2178 needspace = 0;
2179 }
2180 }
2181 else if (*ptr == '\n' && pre)
2182 {
2183 width = 0;
2184 needspace = 0;
2185 ptr ++;
2186 }
2187 else if (isspace((*ptr)&255))
2188 {
2189 needspace = 1;
2190
2191 ptr ++;
2192 }
2193 else if (*ptr == '&' && s < (buf + sizeof(buf) - 1))
2194 {
2195 ptr ++;
2196
2197 int qch = quote_char(ptr);
2198
2199 if (qch < 0)
2200 *s++ = '&';
2201 else {
2202// int l;
2203// l = fl_utf8encode((unsigned int) qch, s);
2204// if (l < 1) l = 1;
2205// s += l;
2206 *s++ = qch;
2207 ptr = strchr(ptr, ';') + 1;
2208 }
2209 }
2210 else
2211 {
2212 if (s < (buf + sizeof(buf) - 1))
2213 *s++ = *ptr++;
2214 else
2215 ptr ++;
2216 }
2217 }
2218
2219 // Now that we have scanned the entire table, adjust the table and
2220 // cell widths to fit on the screen...
2221 if (get_attr(table + 6, "WIDTH", attr, sizeof(attr)))
2222 *table_width = get_length(attr);
2223 else
2224 *table_width = 0;
2225
2226#ifdef DEBUG
2227 printf("num_columns = %d, table_width = %d\n", num_columns, *table_width);
2228#endif // DEBUG
2229
2230 if (num_columns == 0)
2231 return;
2232
2233 // Add up the widths...
2234 for (column = 0, width = 0; column < num_columns; column ++)
2235 width += columns[column];
2236
2237#ifdef DEBUG
2238 printf("width = %d, w() = %d\n", width, w());
2239 for (column = 0; column < num_columns; column ++)
2240 printf(" columns[%d] = %d, minwidths[%d] = %d\n", column, columns[column],
2241 column, minwidths[column]);
2242#endif // DEBUG
2243
2244 // Adjust the width if needed...
2245 int scale_width = *table_width;
2246
2247 int scrollsize = scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size();
2248 if (scale_width == 0) {
2249 if (width > (hsize_ - scrollsize)) scale_width = hsize_ - scrollsize;
2250 else scale_width = width;
2251 }
2252
2253 if (width < scale_width) {
2254#ifdef DEBUG
2255 printf("Scaling table up to %d from %d...\n", scale_width, width);
2256#endif // DEBUG
2257
2258 *table_width = 0;
2259
2260 scale_width = (scale_width - width) / num_columns;
2261
2262#ifdef DEBUG
2263 printf("adjusted scale_width = %d\n", scale_width);
2264#endif // DEBUG
2265
2266 for (column = 0; column < num_columns; column ++) {
2267 columns[column] += scale_width;
2268
2269 (*table_width) += columns[column];
2270 }
2271 }
2272 else if (width > scale_width) {
2273#ifdef DEBUG
2274 printf("Scaling table down to %d from %d...\n", scale_width, width);
2275#endif // DEBUG
2276
2277 for (column = 0; column < num_columns; column ++) {
2278 width -= minwidths[column];
2279 scale_width -= minwidths[column];
2280 }
2281
2282#ifdef DEBUG
2283 printf("adjusted width = %d, scale_width = %d\n", width, scale_width);
2284#endif // DEBUG
2285
2286 if (width > 0) {
2287 for (column = 0; column < num_columns; column ++) {
2288 columns[column] -= minwidths[column];
2289 columns[column] = scale_width * columns[column] / width;
2290 columns[column] += minwidths[column];
2291 }
2292 }
2293
2294 *table_width = 0;
2295 for (column = 0; column < num_columns; column ++) {
2296 (*table_width) += columns[column];
2297 }
2298 }
2299 else if (*table_width == 0)
2300 *table_width = width;
2301
2302#ifdef DEBUG
2303 printf("FINAL table_width = %d\n", *table_width);
2304 for (column = 0; column < num_columns; column ++)
2305 printf(" columns[%d] = %d\n", column, columns[column]);
2306#endif // DEBUG
2307}
2308
2309
2310/** Frees memory used for the document. */
2311void
2312Fl_Help_View::free_data() {
2313 // Release all images...
2314 if (value_) {
2315 const char *ptr, // Pointer into block
2316 *attrs; // Pointer to start of element attributes
2317 char *s, // Pointer into buffer
2318 buf[1024], // Text buffer
2319 attr[1024], // Attribute buffer
2320 wattr[1024], // Width attribute buffer
2321 hattr[1024]; // Height attribute buffer
2322
2323 for (ptr = value_; *ptr;)
2324 {
2325 if (*ptr == '<')
2326 {
2327 ptr ++;
2328
2329 if (strncmp(ptr, "!--", 3) == 0)
2330 {
2331 // Comment...
2332 ptr += 3;
2333 if ((ptr = strstr(ptr, "-->")) != NULL)
2334 {
2335 ptr += 3;
2336 continue;
2337 }
2338 else
2339 break;
2340 }
2341
2342 s = buf;
2343
2344 while (*ptr && *ptr != '>' && !isspace((*ptr)&255))
2345 if (s < (buf + sizeof(buf) - 1))
2346 *s++ = *ptr++;
2347 else
2348 ptr ++;
2349
2350 *s = '\0';
2351
2352 attrs = ptr;
2353 while (*ptr && *ptr != '>')
2354 ptr ++;
2355
2356 if (*ptr == '>')
2357 ptr ++;
2358
2359 if (strcasecmp(buf, "IMG") == 0)
2360 {
2361 Fl_Shared_Image *img;
2362 int width;
2363 int height;
2364
2365 get_attr(attrs, "WIDTH", wattr, sizeof(wattr));
2366 get_attr(attrs, "HEIGHT", hattr, sizeof(hattr));
2367 width = get_length(wattr);
2368 height = get_length(hattr);
2369
2370 if (get_attr(attrs, "SRC", attr, sizeof(attr))) {
2371 // Get and release the image to free it from memory...
2372 img = get_image(attr, width, height);
2373 if ((void*)img != &broken_image) {
2374 img->release();
2375 }
2376 }
2377 }
2378 }
2379 else
2380 ptr ++;
2381 }
2382
2383 free((void *)value_);
2384 value_ = 0;
2385 }
2386
2387 // Free all of the arrays...
2388 if (nblocks_) {
2389 free(blocks_);
2390
2391 ablocks_ = 0;
2392 nblocks_ = 0;
2393 blocks_ = 0;
2394 }
2395
2396 if (nlinks_) {
2397 free(links_);
2398
2399 alinks_ = 0;
2400 nlinks_ = 0;
2401 links_ = 0;
2402 }
2403
2404 if (ntargets_) {
2405 free(targets_);
2406
2407 atargets_ = 0;
2408 ntargets_ = 0;
2409 targets_ = 0;
2410 }
2411}
2412
2413/** Gets an alignment attribute. */
2414int // O - Alignment
2415Fl_Help_View::get_align(const char *p, // I - Pointer to start of attrs
2416 int a) // I - Default alignment
2417{
2418 char buf[255]; // Alignment value
2419
2420
2421 if (get_attr(p, "ALIGN", buf, sizeof(buf)) == NULL)
2422 return (a);
2423
2424 if (strcasecmp(buf, "CENTER") == 0)
2425 return (CENTER);
2426 else if (strcasecmp(buf, "RIGHT") == 0)
2427 return (RIGHT);
2428 else
2429 return (LEFT);
2430}
2431
2432
2433/** Gets an attribute value from the string. */
2434const char * // O - Pointer to buf or NULL
2435Fl_Help_View::get_attr(const char *p, // I - Pointer to start of attributes
2436 const char *n, // I - Name of attribute
2437 char *buf, // O - Buffer for attribute value
2438 int bufsize) // I - Size of buffer
2439{
2440 char name[255], // Name from string
2441 *ptr, // Pointer into name or value
2442 quote; // Quote
2443
2444
2445 buf[0] = '\0';
2446
2447 while (*p && *p != '>')
2448 {
2449 while (isspace((*p)&255))
2450 p ++;
2451
2452 if (*p == '>' || !*p)
2453 return (NULL);
2454
2455 for (ptr = name; *p && !isspace((*p)&255) && *p != '=' && *p != '>';)
2456 if (ptr < (name + sizeof(name) - 1))
2457 *ptr++ = *p++;
2458 else
2459 p ++;
2460
2461 *ptr = '\0';
2462
2463 if (isspace((*p)&255) || !*p || *p == '>')
2464 buf[0] = '\0';
2465 else
2466 {
2467 if (*p == '=')
2468 p ++;
2469
2470 for (ptr = buf; *p && !isspace((*p)&255) && *p != '>';)
2471 if (*p == '\'' || *p == '\"')
2472 {
2473 quote = *p++;
2474
2475 while (*p && *p != quote)
2476 if ((ptr - buf + 1) < bufsize)
2477 *ptr++ = *p++;
2478 else
2479 p ++;
2480
2481 if (*p == quote)
2482 p ++;
2483 }
2484 else if ((ptr - buf + 1) < bufsize)
2485 *ptr++ = *p++;
2486 else
2487 p ++;
2488
2489 *ptr = '\0';
2490 }
2491
2492 if (strcasecmp(n, name) == 0)
2493 return (buf);
2494 else
2495 buf[0] = '\0';
2496
2497 if (*p == '>')
2498 return (NULL);
2499 }
2500
2501 return (NULL);
2502}
2503
2504
2505/** Gets a color attribute. */
2506Fl_Color // O - Color value
2507Fl_Help_View::get_color(const char *n, // I - Color name
2508 Fl_Color c) // I - Default color value
2509{
2510 int i; // Looping var
2511 int rgb, r, g, b; // RGB values
2512 static const struct { // Color name table
2513 const char *name;
2514 int r, g, b;
2515 } colors[] = {
2516 { "black", 0x00, 0x00, 0x00 },
2517 { "red", 0xff, 0x00, 0x00 },
2518 { "green", 0x00, 0x80, 0x00 },
2519 { "yellow", 0xff, 0xff, 0x00 },
2520 { "blue", 0x00, 0x00, 0xff },
2521 { "magenta", 0xff, 0x00, 0xff },
2522 { "fuchsia", 0xff, 0x00, 0xff },
2523 { "cyan", 0x00, 0xff, 0xff },
2524 { "aqua", 0x00, 0xff, 0xff },
2525 { "white", 0xff, 0xff, 0xff },
2526 { "gray", 0x80, 0x80, 0x80 },
2527 { "grey", 0x80, 0x80, 0x80 },
2528 { "lime", 0x00, 0xff, 0x00 },
2529 { "maroon", 0x80, 0x00, 0x00 },
2530 { "navy", 0x00, 0x00, 0x80 },
2531 { "olive", 0x80, 0x80, 0x00 },
2532 { "purple", 0x80, 0x00, 0x80 },
2533 { "silver", 0xc0, 0xc0, 0xc0 },
2534 { "teal", 0x00, 0x80, 0x80 }
2535 };
2536
2537
2538 if (!n || !n[0]) return c;
2539
2540 if (n[0] == '#') {
2541 // Do hex color lookup
2542 rgb = strtol(n + 1, NULL, 16);
2543
2544 if (strlen(n) > 4) {
2545 r = rgb >> 16;
2546 g = (rgb >> 8) & 255;
2547 b = rgb & 255;
2548 } else {
2549 r = (rgb >> 8) * 17;
2550 g = ((rgb >> 4) & 15) * 17;
2551 b = (rgb & 15) * 17;
2552 }
2553 return (fl_rgb_color((uchar)r, (uchar)g, (uchar)b));
2554 } else {
2555 for (i = 0; i < (int)(sizeof(colors) / sizeof(colors[0])); i ++)
2556 if (!strcasecmp(n, colors[i].name)) {
2557 return fl_rgb_color(colors[i].r, colors[i].g, colors[i].b);
2558 }
2559 return c;
2560 }
2561}
2562
2563
2564/** Gets an inline image.
2565
2566 The image reference count is maintained accordingly, such that
2567 the image can be released exactly once when the document is closed.
2568
2569 \return a pointer to a cached Fl_Shared_Image, if the image can be loaded,
2570 otherwise a pointer to an internal Fl_Pixmap (broken_image).
2571
2572 \todo Fl_Help_View::get_image() returns a pointer to the internal
2573 Fl_Pixmap broken_image, but this is _not_ compatible with the
2574 return type Fl_Shared_Image (release() must not be called).
2575*/
2576
2577/* Implementation note: (A.S. Apr 05, 2009)
2578
2579 Fl_Help_View::get_image() uses a static global flag (initial_load)
2580 to determine, if it is called from the initial loading of a document
2581 (load() or value()), or from resize() or draw().
2582
2583 A better solution would be to manage all loaded images in an own
2584 structure like Fl_Help_Target (Fl_Help_Image ?) to avoid using this
2585 global flag, but this would break the ABI !
2586
2587 This should be fixed in FLTK 1.3 !
2588
2589
2590 If initial_load is true, then Fl_Shared_Image::get() is called to
2591 load the image, and the reference count of the shared image is
2592 increased by one.
2593
2594 If initial_load is false, then Fl_Shared_Image::find() is called to
2595 load the image, and the image is released immediately. This avoids
2596 increasing the reference count when calling get_image() from draw()
2597 or resize().
2598
2599 Calling Fl_Shared_Image::find() instead of Fl_Shared_Image::get() avoids
2600 doing unnecessary i/o for "broken images" within each resize/redraw.
2601
2602 Each image must be released exactly once in the destructor or before
2603 a new document is loaded: see free_data().
2604*/
2605
2606Fl_Shared_Image *
2607Fl_Help_View::get_image(const char *name, int W, int H) {
2608 const char *localname; // Local filename
2609 char dir[FL_PATH_MAX]; // Current directory
2610 char temp[FL_PATH_MAX], // Temporary filename
2611 *tempptr; // Pointer into temporary name
2612 Fl_Shared_Image *ip; // Image pointer...
2613
2614 // See if the image can be found...
2615 if (strchr(directory_, ':') != NULL && strchr(name, ':') == NULL) {
2616 if (name[0] == '/') {
2617 strlcpy(temp, directory_, sizeof(temp));
2618
2619 if ((tempptr = strrchr(strchr(directory_, ':') + 3, '/')) != NULL) {
2620 strlcpy(tempptr, name, sizeof(temp) - (tempptr - temp));
2621 } else {
2622 strlcat(temp, name, sizeof(temp));
2623 }
2624 } else {
2625 snprintf(temp, sizeof(temp), "%s/%s", directory_, name);
2626 }
2627
2628 if (link_) localname = (*link_)(this, temp);
2629 else localname = temp;
2630 } else if (name[0] != '/' && strchr(name, ':') == NULL) {
2631 if (directory_[0]) snprintf(temp, sizeof(temp), "%s/%s", directory_, name);
2632 else {
2633 fl_getcwd(dir, sizeof(dir));
2634 snprintf(temp, sizeof(temp), "file:%s/%s", dir, name);
2635 }
2636
2637 if (link_) localname = (*link_)(this, temp);
2638 else localname = temp;
2639 } else if (link_) localname = (*link_)(this, name);
2640 else localname = name;
2641
2642 if (!localname) return 0;
2643
2644 if (strncmp(localname, "file:", 5) == 0) localname += 5;
2645
2646 if (initial_load) {
2647 if ((ip = Fl_Shared_Image::get(localname, W, H)) == NULL) {
2648 ip = (Fl_Shared_Image *)&broken_image;
2649 }
2650 } else { // draw or resize
2651 if ((ip = Fl_Shared_Image::find(localname, W, H)) == NULL) {
2652 ip = (Fl_Shared_Image *)&broken_image;
2653 } else {
2654 ip->release();
2655 }
2656 }
2657
2658 return ip;
2659}
2660
2661
2662/** Gets a length value, either absolute or %. */
2663int
2664Fl_Help_View::get_length(const char *l) { // I - Value
2665 int val; // Integer value
2666
2667 if (!l[0]) return 0;
2668
2669 val = atoi(l);
2670 if (l[strlen(l) - 1] == '%') {
2671 if (val > 100) val = 100;
2672 else if (val < 0) val = 0;
2673
2674 int scrollsize = scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size();
2675 val = val * (hsize_ - scrollsize) / 100;
2676 }
2677
2678 return val;
2679}
2680
2681
2682Fl_Help_Link *Fl_Help_View::find_link(int xx, int yy)
2683{
2684 int i;
2685 Fl_Help_Link *linkp;
2686 for (i = nlinks_, linkp = links_; i > 0; i --, linkp ++) {
2687 if (xx >= linkp->x && xx < linkp->w &&
2688 yy >= linkp->y && yy < linkp->h)
2689 break;
2690 }
2691 return i ? linkp : 0L;
2692}
2693
2694void Fl_Help_View::follow_link(Fl_Help_Link *linkp)
2695{
2696 char target[32]; // Current target
2697
2698 clear_selection();
2699
2700 strlcpy(target, linkp->name, sizeof(target));
2701
2702 set_changed();
2703
2704 if (strcmp(linkp->filename, filename_) != 0 && linkp->filename[0])
2705 {
2706 char dir[FL_PATH_MAX]; // Current directory
2707 char temp[FL_PATH_MAX], // Temporary filename
2708 *tempptr; // Pointer into temporary filename
2709
2710
2711 if (strchr(directory_, ':') != NULL &&
2712 strchr(linkp->filename, ':') == NULL)
2713 {
2714 if (linkp->filename[0] == '/')
2715 {
2716 strlcpy(temp, directory_, sizeof(temp));
2717 if ((tempptr = strrchr(strchr(directory_, ':') + 3, '/')) != NULL)
2718 strlcpy(tempptr, linkp->filename, sizeof(temp));
2719 else
2720 strlcat(temp, linkp->filename, sizeof(temp));
2721 }
2722 else
2723 snprintf(temp, sizeof(temp), "%s/%s", directory_, linkp->filename);
2724 }
2725 else if (linkp->filename[0] != '/' && strchr(linkp->filename, ':') == NULL)
2726 {
2727 if (directory_[0])
2728 snprintf(temp, sizeof(temp), "%s/%s", directory_, linkp->filename);
2729 else
2730 {
2731 fl_getcwd(dir, sizeof(dir));
2732 snprintf(temp, sizeof(temp), "file:%s/%s", dir, linkp->filename);
2733 }
2734 }
2735 else
2736 strlcpy(temp, linkp->filename, sizeof(temp));
2737
2738 if (linkp->name[0])
2739 snprintf(temp + strlen(temp), sizeof(temp) - strlen(temp), "#%s",
2740 linkp->name);
2741
2742 load(temp);
2743 }
2744 else if (target[0])
2745 topline(target);
2746 else
2747 topline(0);
2748
2749 leftline(0);
2750}
2751
2752/** Removes the current text selection. */
2753void Fl_Help_View::clear_selection()
2754{
2755 if (current_view==this)
2756 clear_global_selection();
2757}
2758/** Selects all the text in the view. */
2759void Fl_Help_View::select_all()
2760{
2761 clear_global_selection();
2762 if (!value_) return;
2763 current_view = this;
2764 selection_drag_last = selection_last = strlen(value_);
2765 selected = 1;
2766}
2767
2768void Fl_Help_View::clear_global_selection()
2769{
2770 if (selected) redraw();
2771 selection_push_first = selection_push_last = 0;
2772 selection_drag_first = selection_drag_last = 0;
2773 selection_first = selection_last = 0;
2774 selected = 0;
2775}
2776
2777char Fl_Help_View::begin_selection()
2778{
2779 clear_global_selection();
2780
2781 if (!fl_help_view_buffer) fl_help_view_buffer = fl_create_offscreen(1, 1);
2782
2783 mouse_x = Fl::event_x();
2784 mouse_y = Fl::event_y();
2785 draw_mode = 1;
2786
2787 current_view = this;
2788 fl_begin_offscreen(fl_help_view_buffer);
2789 draw();
2790 fl_end_offscreen();
2791
2792 draw_mode = 0;
2793
2794 if (selection_push_last) return 1;
2795 else return 0;
2796}
2797
2798char Fl_Help_View::extend_selection()
2799{
2800 if (Fl::event_is_click())
2801 return 0;
2802
2803// printf("old selection_first=%d, selection_last=%d\n",
2804// selection_first, selection_last);
2805
2806 int sf = selection_first, sl = selection_last;
2807
2808 selected = 1;
2809 mouse_x = Fl::event_x();
2810 mouse_y = Fl::event_y();
2811 draw_mode = 2;
2812
2813 fl_begin_offscreen(fl_help_view_buffer);
2814 draw();
2815 fl_end_offscreen();
2816
2817 draw_mode = 0;
2818
2819 if (selection_push_first < selection_drag_first) {
2820 selection_first = selection_push_first;
2821 } else {
2822 selection_first = selection_drag_first;
2823 }
2824
2825 if (selection_push_last > selection_drag_last) {
2826 selection_last = selection_push_last;
2827 } else {
2828 selection_last = selection_drag_last;
2829 }
2830
2831// printf("new selection_first=%d, selection_last=%d\n",
2832// selection_first, selection_last);
2833
2834 if (sf!=selection_first || sl!=selection_last) {
2835// puts("REDRAW!!!\n");
2836 return 1;
2837 } else {
2838// puts("");
2839 return 0;
2840 }
2841}
2842
2843// convert a command with up to four letters into an unsigned int
2844static unsigned int command(const char *cmd)
2845{
2846 unsigned int ret = (tolower(cmd[0])<<24);
2847 char c = cmd[1];
2848 if (c=='>' || c==' ' || c==0) return ret;
2849 ret |= (tolower(c)<<16);
2850 c = cmd[2];
2851 if (c=='>' || c==' ' || c==0) return ret;
2852 ret |= (tolower(c)<<8);
2853 c = cmd[3];
2854 if (c=='>' || c==' ' || c==0) return ret;
2855 ret |= tolower(c);
2856 c = cmd[4];
2857 if (c=='>' || c==' ' || c==0) return ret;
2858 return 0;
2859}
2860
2861#define CMD(a, b, c, d) ((a<<24)|(b<<16)|(c<<8)|d)
2862
2863void Fl_Help_View::end_selection(int clipboard)
2864{
2865 if (!selected || current_view!=this)
2866 return;
2867 // convert the select part of our html text into some kind of somewhat readable ASCII
2868 // and store it in the selection buffer
2869 char p = 0, pre = 0;;
2870 int len = strlen(value_);
2871 char *txt = (char*)malloc(len+1), *d = txt;
2872 const char *s = value_, *cmd, *src;
2873 for (;;) {
2874 char c = *s++;
2875 if (c==0) break;
2876 if (c=='<') { // begin of some html command. Skip until we find a '>'
2877 cmd = s;
2878 for (;;) {
2879 c = *s++;
2880 if (c==0 || c=='>') break;
2881 }
2882 if (c==0) break;
2883 // do something with this command... .
2884 // the replacement string must not be longer that the command itself plus '<' and '>'
2885 src = 0;
2886 switch (command(cmd)) {
2887 case CMD('p','r','e', 0 ): pre = 1; break;
2888 case CMD('/','p','r','e'): pre = 0; break;
2889 case CMD('t','d', 0 , 0 ):
2890 case CMD('p', 0 , 0 , 0 ):
2891 case CMD('/','p', 0 , 0 ):
2892 case CMD('b','r', 0 , 0 ): src = "\n"; break;
2893 case CMD('l','i', 0 , 0 ): src = "\n * "; break;
2894 case CMD('/','h','1', 0 ):
2895 case CMD('/','h','2', 0 ):
2896 case CMD('/','h','3', 0 ):
2897 case CMD('/','h','4', 0 ):
2898 case CMD('/','h','5', 0 ):
2899 case CMD('/','h','6', 0 ): src = "\n\n"; break;
2900 case CMD('t','r', 0 , 0 ):
2901 case CMD('h','1', 0 , 0 ):
2902 case CMD('h','2', 0 , 0 ):
2903 case CMD('h','3', 0 , 0 ):
2904 case CMD('h','4', 0 , 0 ):
2905 case CMD('h','5', 0 , 0 ):
2906 case CMD('h','6', 0 , 0 ): src = "\n\n"; break;
2907 case CMD('d','t', 0 , 0 ): src = "\n "; break;
2908 case CMD('d','d', 0 , 0 ): src = "\n - "; break;
2909 }
2910 int n = s-value_;
2911 if (src && n>selection_first && n<=selection_last) {
2912 while (*src) {
2913 *d++ = *src++;
2914 }
2915 c = src[-1];
2916 p = isspace(c&255) ? ' ' : c;
2917 }
2918 continue;
2919 }
2920 if (c=='&') { // special characters
2921 int xx = quote_char(s);
2922 if (xx>=0) {
2923 c = (char)xx;
2924 for (;;) {
2925 char cc = *s++;
2926 if (!cc || cc==';') break;
2927 }
2928 }
2929 }
2930 int n = s-value_;
2931 if (n>selection_first && n<=selection_last) {
2932 if (!pre && isspace(c&255)) c = ' ';
2933 if (p!=' '||c!=' ')
2934 *d++ = c;
2935 p = c;
2936 }
2937 }
2938 *d = 0;
2939 Fl::copy(txt, strlen(txt), clipboard);
2940 free(txt);
2941}
2942
2943#define ctrl(x) ((x)&0x1f)
2944
2945/** Handles events in the widget. */
2946int // O - 1 if we handled it, 0 otherwise
2947Fl_Help_View::handle(int event) // I - Event to handle
2948{
2949 static Fl_Help_Link *linkp; // currently clicked link
2950
2951 int xx = Fl::event_x() - x() + leftline_;
2952 int yy = Fl::event_y() - y() + topline_;
2953
2954 switch (event)
2955 {
2956 case FL_FOCUS:
2957 redraw();
2958 return 1;
2959 case FL_UNFOCUS:
2960 clear_selection();
2961 redraw();
2962 return 1;
2963 case FL_ENTER :
2964 Fl_Group::handle(event);
2965 return 1;
2966 case FL_LEAVE :
2967 fl_cursor(FL_CURSOR_DEFAULT);
2968 break;
2969 case FL_MOVE:
2970 if (find_link(xx, yy)) fl_cursor(FL_CURSOR_HAND);
2971 else fl_cursor(FL_CURSOR_DEFAULT);
2972 return 1;
2973 case FL_PUSH:
2974 if (Fl_Group::handle(event)) return 1;
2975 linkp = find_link(xx, yy);
2976 if (linkp) {
2977 fl_cursor(FL_CURSOR_HAND);
2978 return 1;
2979 }
2980 if (begin_selection()) {
2981 fl_cursor(FL_CURSOR_INSERT);
2982 return 1;
2983 }
2984 fl_cursor(FL_CURSOR_DEFAULT);
2985 return 1;
2986 case FL_DRAG:
2987 if (linkp) {
2988 if (Fl::event_is_click()) {
2989 fl_cursor(FL_CURSOR_HAND);
2990 } else {
2991 fl_cursor(FL_CURSOR_DEFAULT); // should be "FL_CURSOR_CANCEL" if we had it
2992 }
2993 return 1;
2994 }
2995 if (current_view==this && selection_push_last) {
2996 if (extend_selection()) redraw();
2997 fl_cursor(FL_CURSOR_INSERT);
2998 return 1;
2999 }
3000 fl_cursor(FL_CURSOR_DEFAULT);
3001 return 1;
3002 case FL_RELEASE:
3003 if (linkp) {
3004 if (Fl::event_is_click()) {
3005 follow_link(linkp);
3006 }
3007 fl_cursor(FL_CURSOR_DEFAULT);
3008 linkp = 0;
3009 return 1;
3010 }
3011 if (current_view==this && selection_push_last) {
3012 end_selection();
3013 return 1;
3014 }
3015 return 1;
3016 case FL_SHORTCUT: {
3017 char ascii = Fl::event_text()[0];
3018 switch (ascii) {
3019 case ctrl('A'): select_all(); redraw(); return 1;
3020 case ctrl('C'):
3021 case ctrl('X'): end_selection(1); return 1;
3022 }
3023 break; }
3024 }
3025 return (Fl_Group::handle(event));
3026}
3027
3028/**
3029 The constructor creates the Fl_Help_View widget at the specified
3030 position and size.
3031*/
3032Fl_Help_View::Fl_Help_View(int xx, // I - Left position
3033 int yy, // I - Top position
3034 int ww, // I - Width in pixels
3035 int hh, // I - Height in pixels
3036 const char *l)
3037 : Fl_Group(xx, yy, ww, hh, l),
3038 scrollbar_(xx + ww - Fl::scrollbar_size(), yy,
3039 Fl::scrollbar_size(), hh - Fl::scrollbar_size()),
3040 hscrollbar_(xx, yy + hh - Fl::scrollbar_size(),
3041 ww - Fl::scrollbar_size(), Fl::scrollbar_size())
3042{
3043 color(FL_BACKGROUND2_COLOR, FL_SELECTION_COLOR);
3044
3045 title_[0] = '\0';
3046 defcolor_ = FL_FOREGROUND_COLOR;
3047 bgcolor_ = FL_BACKGROUND_COLOR;
3048 textcolor_ = FL_FOREGROUND_COLOR;
3049 linkcolor_ = FL_SELECTION_COLOR;
3050 textfont_ = FL_TIMES;
3051 textsize_ = 12;
3052 value_ = NULL;
3053
3054 ablocks_ = 0;
3055 nblocks_ = 0;
3056 blocks_ = (Fl_Help_Block *)0;
3057
3058 link_ = (Fl_Help_Func *)0;
3059
3060 alinks_ = 0;
3061 nlinks_ = 0;
3062 links_ = (Fl_Help_Link *)0;
3063
3064 atargets_ = 0;
3065 ntargets_ = 0;
3066 targets_ = (Fl_Help_Target *)0;
3067
3068 directory_[0] = '\0';
3069 filename_[0] = '\0';
3070
3071 topline_ = 0;
3072 leftline_ = 0;
3073 size_ = 0;
3074 hsize_ = 0;
3075 scrollbar_size_ = 0;
3076
3077 scrollbar_.value(0, hh, 0, 1);
3078 scrollbar_.step(8.0);
3079 scrollbar_.show();
3080 scrollbar_.callback(scrollbar_callback);
3081
3082 hscrollbar_.value(0, ww, 0, 1);
3083 hscrollbar_.step(8.0);
3084 hscrollbar_.show();
3085 hscrollbar_.callback(hscrollbar_callback);
3086 hscrollbar_.type(FL_HORIZONTAL);
3087 end();
3088
3089 resize(xx, yy, ww, hh);
3090}
3091
3092
3093/** Destroys the Fl_Help_View widget.
3094
3095 The destructor destroys the widget and frees all memory that has been
3096 allocated for the current document.
3097*/
3098Fl_Help_View::~Fl_Help_View()
3099{
3100 clear_selection();
3101 free_data();
3102}
3103
3104
3105/** Loads the specified file.
3106
3107 This method loads the specified file or URL.
3108*/
3109int // O - 0 on success, -1 on error
3110Fl_Help_View::load(const char *f)// I - Filename to load (may also have target)
3111{
3112 FILE *fp; // File to read from
3113 long len; // Length of file
3114 char *target; // Target in file
3115 char *slash; // Directory separator
3116 const char *localname; // Local filename
3117 char error[1024]; // Error buffer
3118 char newname[FL_PATH_MAX]; // New filename buffer
3119
3120 // printf("load(%s)\n",f); fflush(stdout);
3121
3122 if (strncmp(f, "ftp:", 4) == 0 ||
3123 strncmp(f, "http:", 5) == 0 ||
3124 strncmp(f, "https:", 6) == 0 ||
3125 strncmp(f, "ipp:", 4) == 0 ||
3126 strncmp(f, "mailto:", 7) == 0 ||
3127 strncmp(f, "news:", 5) == 0) {
3128 char urimsg[FL_PATH_MAX];
3129 if ( fl_open_uri(f, urimsg, sizeof(urimsg)) == 0 ) {
3130 clear_selection();
3131
3132 strlcpy(newname, f, sizeof(newname));
3133 if ((target = strrchr(newname, '#')) != NULL)
3134 *target++ = '\0';
3135
3136 if (link_)
3137 localname = (*link_)(this, newname);
3138 else
3139 localname = filename_;
3140
3141 if (!localname)
3142 return (0);
3143
3144 free_data();
3145
3146 strlcpy(filename_, newname, sizeof(filename_));
3147 strlcpy(directory_, newname, sizeof(directory_));
3148
3149 // Note: We do not support Windows backslashes, since they are illegal
3150 // in URLs...
3151 if ((slash = strrchr(directory_, '/')) == NULL)
3152 directory_[0] = '\0';
3153 else if (slash > directory_ && slash[-1] != '/')
3154 *slash = '\0';
3155
3156 snprintf(error, sizeof(error),
3157 "<HTML><HEAD><TITLE>Error</TITLE></HEAD>"
3158 "<BODY><H1>Error</H1>"
3159 "<P>Unable to follow the link \"%s\" - "
3160 "%s.</P></BODY>",
3161 f, urimsg);
3162 value(error);
3163 //return(-1);
3164 }
3165 return(0);
3166 }
3167
3168 clear_selection();
3169
3170 strlcpy(newname, f, sizeof(newname));
3171 if ((target = strrchr(newname, '#')) != NULL)
3172 *target++ = '\0';
3173
3174 if (link_)
3175 localname = (*link_)(this, newname);
3176 else
3177 localname = filename_;
3178
3179 if (!localname)
3180 return (0);
3181
3182 free_data();
3183
3184 strlcpy(filename_, newname, sizeof(filename_));
3185 strlcpy(directory_, newname, sizeof(directory_));
3186
3187 // Note: We do not support Windows backslashes, since they are illegal
3188 // in URLs...
3189 if ((slash = strrchr(directory_, '/')) == NULL)
3190 directory_[0] = '\0';
3191 else if (slash > directory_ && slash[-1] != '/')
3192 *slash = '\0';
3193
3194 if (strncmp(localname, "file:", 5) == 0)
3195 localname += 5; // Adjust for local filename...
3196
3197 if ((fp = fl_fopen(localname, "rb")) != NULL)
3198 {
3199 fseek(fp, 0, SEEK_END);
3200 len = ftell(fp);
3201 rewind(fp);
3202
3203 value_ = (const char *)calloc(len + 1, 1);
3204 if (fread((void *)value_, 1, len, fp)==0) { /* use default 0 */ }
3205 fclose(fp);
3206 }
3207 else
3208 {
3209 snprintf(error, sizeof(error),
3210 "<HTML><HEAD><TITLE>Error</TITLE></HEAD>"
3211 "<BODY><H1>Error</H1>"
3212 "<P>Unable to follow the link \"%s\" - "
3213 "%s.</P></BODY>",
3214 localname, strerror(errno));
3215 value_ = strdup(error);
3216 }
3217
3218 initial_load = 1;
3219 format();
3220 initial_load = 0;
3221
3222 if (target)
3223 topline(target);
3224 else
3225 topline(0);
3226
3227 return (0);
3228}
3229
3230
3231/** Resizes the help widget. */
3232
3233void
3234Fl_Help_View::resize(int xx, // I - New left position
3235 int yy, // I - New top position
3236 int ww, // I - New width
3237 int hh) // I - New height
3238{
3239 Fl_Boxtype b = box() ? box() : FL_DOWN_BOX;
3240 // Box to draw...
3241
3242
3243 Fl_Widget::resize(xx, yy, ww, hh);
3244
3245 int scrollsize = scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size();
3246 scrollbar_.resize(x() + w() - scrollsize - Fl::box_dw(b) + Fl::box_dx(b),
3247 y() + Fl::box_dy(b), scrollsize, h() - scrollsize - Fl::box_dh(b));
3248 hscrollbar_.resize(x() + Fl::box_dx(b),
3249 y() + h() - scrollsize - Fl::box_dh(b) + Fl::box_dy(b),
3250 w() - scrollsize - Fl::box_dw(b), scrollsize);
3251
3252 format();
3253}
3254
3255
3256/** Scrolls the text to the indicated position, given a named destination.
3257
3258 \param[in] n target name
3259*/
3260void
3261Fl_Help_View::topline(const char *n) // I - Target name
3262{
3263 Fl_Help_Target key, // Target name key
3264 *target; // Pointer to matching target
3265
3266
3267 if (ntargets_ == 0)
3268 return;
3269
3270 strlcpy(key.name, n, sizeof(key.name));
3271
3272 target = (Fl_Help_Target *)bsearch(&key, targets_, ntargets_, sizeof(Fl_Help_Target),
3273 (compare_func_t)compare_targets);
3274
3275 if (target != NULL)
3276 topline(target->y);
3277}
3278
3279
3280/** Scrolls the text to the indicated position, given a pixel line.
3281
3282 If the given pixel value \p top is out of range, then the text is
3283 scrolled to the top or bottom of the document, resp.
3284
3285 \param[in] top top line number in pixels (0 = start of document)
3286*/
3287void
3288Fl_Help_View::topline(int top) // I - Top line number
3289{
3290 if (!value_)
3291 return;
3292
3293 int scrollsize = scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size();
3294 if (size_ < (h() - scrollsize) || top < 0)
3295 top = 0;
3296 else if (top > size_)
3297 top = size_;
3298
3299 topline_ = top;
3300
3301 scrollbar_.value(topline_, h() - scrollsize, 0, size_);
3302
3303 do_callback();
3304
3305 redraw();
3306}
3307
3308
3309/** Scrolls the text to the indicated position, given a pixel column.
3310
3311 If the given pixel value \p left is out of range, then the text is
3312 scrolled to the left or right side of the document, resp.
3313
3314 \param[in] left left column number in pixels (0 = left side)
3315*/
3316void
3317Fl_Help_View::leftline(int left) // I - Left position
3318{
3319 if (!value_)
3320 return;
3321
3322 int scrollsize = scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size();
3323 if (hsize_ < (w() - scrollsize) || left < 0)
3324 left = 0;
3325 else if (left > hsize_)
3326 left = hsize_;
3327
3328 leftline_ = left;
3329
3330 hscrollbar_.value(leftline_, w() - scrollsize, 0, hsize_);
3331
3332 redraw();
3333}
3334
3335
3336/** Sets the current help text buffer to the string provided and reformats the text.
3337
3338 The provided character string \p val is copied internally and will be
3339 freed when value() is called again, or when the widget is destroyed.
3340
3341 If \p val is NULL, then the widget is cleared.
3342*/
3343void
3344Fl_Help_View::value(const char *val) // I - Text to view
3345{
3346 clear_selection();
3347 free_data();
3348 set_changed();
3349
3350 if (!val)
3351 return;
3352
3353 value_ = strdup(val);
3354
3355 initial_load = 1;
3356 format();
3357 initial_load = 0;
3358
3359 topline(0);
3360 leftline(0);
3361}
3362
3363
3364#ifdef ENC
3365# undef ENC
3366#endif
3367// part b in the table seems to be mac_roman - beku
3368# define ENC(a, b) a
3369
3370
3371/** Returns the character code associated with a quoted char. */
3372static int // O - Code or -1 on error
3373quote_char(const char *p) { // I - Quoted string
3374 int i; // Looping var
3375 static struct {
3376 const char *name;
3377 int namelen;
3378 int code;
3379 } *nameptr, // Pointer into name array
3380 names[] = { // Quoting names
3381 { "Aacute;", 7, ENC(193,231) },
3382 { "aacute;", 7, ENC(225,135) },
3383 { "Acirc;", 6, ENC(194,229) },
3384 { "acirc;", 6, ENC(226,137) },
3385 { "acute;", 6, ENC(180,171) },
3386 { "AElig;", 6, ENC(198,174) },
3387 { "aelig;", 6, ENC(230,190) },
3388 { "Agrave;", 7, ENC(192,203) },
3389 { "agrave;", 7, ENC(224,136) },
3390 { "amp;", 4, ENC('&','&') },
3391 { "Aring;", 6, ENC(197,129) },
3392 { "aring;", 6, ENC(229,140) },
3393 { "Atilde;", 7, ENC(195,204) },
3394 { "atilde;", 7, ENC(227,139) },
3395 { "Auml;", 5, ENC(196,128) },
3396 { "auml;", 5, ENC(228,138) },
3397 { "brvbar;", 7, ENC(166, -1) },
3398 { "bull;", 5, ENC(149,165) },
3399 { "Ccedil;", 7, ENC(199,199) },
3400 { "ccedil;", 7, ENC(231,141) },
3401 { "cedil;", 6, ENC(184,252) },
3402 { "cent;", 5, ENC(162,162) },
3403 { "copy;", 5, ENC(169,169) },
3404 { "curren;", 7, ENC(164, -1) },
3405 { "deg;", 4, ENC(176,161) },
3406 { "divide;", 7, ENC(247,214) },
3407 { "Eacute;", 7, ENC(201,131) },
3408 { "eacute;", 7, ENC(233,142) },
3409 { "Ecirc;", 6, ENC(202,230) },
3410 { "ecirc;", 6, ENC(234,144) },
3411 { "Egrave;", 7, ENC(200,233) },
3412 { "egrave;", 7, ENC(232,143) },
3413 { "ETH;", 4, ENC(208, -1) },
3414 { "eth;", 4, ENC(240, -1) },
3415 { "Euml;", 5, ENC(203,232) },
3416 { "euml;", 5, ENC(235,145) },
3417 { "euro;", 5, ENC(128,219) },
3418 { "frac12;", 7, ENC(189, -1) },
3419 { "frac14;", 7, ENC(188, -1) },
3420 { "frac34;", 7, ENC(190, -1) },
3421 { "gt;", 3, ENC('>','>') },
3422 { "Iacute;", 7, ENC(205,234) },
3423 { "iacute;", 7, ENC(237,146) },
3424 { "Icirc;", 6, ENC(206,235) },
3425 { "icirc;", 6, ENC(238,148) },
3426 { "iexcl;", 6, ENC(161,193) },
3427 { "Igrave;", 7, ENC(204,237) },
3428 { "igrave;", 7, ENC(236,147) },
3429 { "iquest;", 7, ENC(191,192) },
3430 { "Iuml;", 5, ENC(207,236) },
3431 { "iuml;", 5, ENC(239,149) },
3432 { "laquo;", 6, ENC(171,199) },
3433 { "lt;", 3, ENC('<','<') },
3434 { "macr;", 5, ENC(175,248) },
3435 { "micro;", 6, ENC(181,181) },
3436 { "middot;", 7, ENC(183,225) },
3437 { "nbsp;", 5, ENC(' ',' ') },
3438 { "not;", 4, ENC(172,194) },
3439 { "Ntilde;", 7, ENC(209,132) },
3440 { "ntilde;", 7, ENC(241,150) },
3441 { "Oacute;", 7, ENC(211,238) },
3442 { "oacute;", 7, ENC(243,151) },
3443 { "Ocirc;", 6, ENC(212,239) },
3444 { "ocirc;", 6, ENC(244,153) },
3445 { "Ograve;", 7, ENC(210,241) },
3446 { "ograve;", 7, ENC(242,152) },
3447 { "ordf;", 5, ENC(170,187) },
3448 { "ordm;", 5, ENC(186,188) },
3449 { "Oslash;", 7, ENC(216,175) },
3450 { "oslash;", 7, ENC(248,191) },
3451 { "Otilde;", 7, ENC(213,205) },
3452 { "otilde;", 7, ENC(245,155) },
3453 { "Ouml;", 5, ENC(214,133) },
3454 { "ouml;", 5, ENC(246,154) },
3455 { "para;", 5, ENC(182,166) },
3456 { "premil;", 7, ENC(137,228) },
3457 { "plusmn;", 7, ENC(177,177) },
3458 { "pound;", 6, ENC(163,163) },
3459 { "quot;", 5, ENC('\"','\"') },
3460 { "raquo;", 6, ENC(187,200) },
3461 { "reg;", 4, ENC(174,168) },
3462 { "sect;", 5, ENC(167,164) },
3463 { "shy;", 4, ENC(173,'-') },
3464 { "sup1;", 5, ENC(185, -1) },
3465 { "sup2;", 5, ENC(178, -1) },
3466 { "sup3;", 5, ENC(179, -1) },
3467 { "szlig;", 6, ENC(223,167) },
3468 { "THORN;", 6, ENC(222, -1) },
3469 { "thorn;", 6, ENC(254, -1) },
3470 { "times;", 6, ENC(215,'x') },
3471 { "trade;", 6, ENC(153,170) },
3472 { "Uacute;", 7, ENC(218,242) },
3473 { "uacute;", 7, ENC(250,156) },
3474 { "Ucirc;", 6, ENC(219,243) },
3475 { "ucirc;", 6, ENC(251,158) },
3476 { "Ugrave;", 7, ENC(217,244) },
3477 { "ugrave;", 7, ENC(249,157) },
3478 { "uml;", 4, ENC(168,172) },
3479 { "Uuml;", 5, ENC(220,134) },
3480 { "uuml;", 5, ENC(252,159) },
3481 { "Yacute;", 7, ENC(221, -1) },
3482 { "yacute;", 7, ENC(253, -1) },
3483 { "yen;", 4, ENC(165,180) },
3484 { "Yuml;", 5, ENC(159,217) },
3485 { "yuml;", 5, ENC(255,216) }
3486 };
3487
3488 if (!strchr(p, ';')) return -1;
3489 if (*p == '#') {
3490 if (*(p+1) == 'x' || *(p+1) == 'X') return strtol(p+2, NULL, 16);
3491 else return atoi(p+1);
3492 }
3493 for (i = (int)(sizeof(names) / sizeof(names[0])), nameptr = names; i > 0; i --, nameptr ++)
3494 if (strncmp(p, nameptr->name, nameptr->namelen) == 0)
3495 return nameptr->code;
3496
3497 return -1;
3498}
3499
3500
3501/** The vertical scrollbar callback. */
3502static void
3503scrollbar_callback(Fl_Widget *s, void *)
3504{
3505 ((Fl_Help_View *)(s->parent()))->topline(int(((Fl_Scrollbar*)s)->value()));
3506}
3507
3508
3509/** The horizontal scrollbar callback. */
3510static void
3511hscrollbar_callback(Fl_Widget *s, void *)
3512{
3513 ((Fl_Help_View *)(s->parent()))->leftline(int(((Fl_Scrollbar*)s)->value()));
3514}
3515
3516
3517//
3518// End of "$Id: Fl_Help_View.cxx 8063 2010-12-19 21:20:10Z matt $".
3519//