Fl_Panzoomer: Always draw box.
[ntk.git] / src / Fl_Help_View.cxx
blob1b31a898277f418103bba283a00daa0d590d5177
1 //
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.
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.
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.
24 // Please report all bugs and problems on the following page:
26 // http://www.fltk.org/str.php
28 // Contents:
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.
55 // Include necessary header files...
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>
71 #if defined(WIN32) && ! defined(__CYGWIN__)
72 # include <io.h>
73 # include <direct.h>
74 #else
75 # include <unistd.h>
76 #endif // WIN32
78 #define MAX_COLUMNS 200
82 // Typedef the C API sort function type the only way I know how...
85 extern "C"
87 typedef int (*compare_func_t)(const void *, const void *);
92 // Local functions...
95 static int quote_char(const char *);
96 static void scrollbar_callback(Fl_Widget *s, void *);
97 static void hscrollbar_callback(Fl_Widget *s, void *);
100 // global flag for image loading (see get_image).
103 static char initial_load = 0;
106 // Broken image...
109 static const char *broken_xpm[] =
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
144 static Fl_Pixmap broken_image(broken_xpm);
147 // Simple margin stack for Fl_Help_View::format()...
150 struct fl_margins {
151 int depth_;
152 int margins_[100];
154 fl_margins() { clear(); }
156 int clear() {
157 // puts("fl_margins::clear()");
159 depth_ = 0;
160 return margins_[0] = 4;
163 int current() { return margins_[depth_]; }
165 int pop() {
166 // printf("fl_margins::pop(): depth_=%d, xx=%d\n", depth_,
167 // depth_ > 0 ? margins_[depth_ - 1] : 4);
169 if (depth_ > 0) {
170 depth_ --;
171 return margins_[depth_];
172 } else return 4;
175 int push(int indent) {
176 int xx;
178 xx = margins_[depth_] + indent;
180 // printf("fl_margins::push(indent=%d): depth_=%d, xx=%d\n", indent,
181 // depth_ + 1, xx);
183 if (depth_ < 99) {
184 depth_ ++;
185 margins_[depth_] = xx;
188 return xx;
193 // All the stuff needed to implement text selection in Fl_Help_View
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.
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
212 The following functions are also used to draw stuff and should be replaced with
213 local copies that are much faster when merely counting:
215 fl_color(Fl_Color);
216 fl_rectf(int, int, int, int);
217 fl_push_clip(int, int, int, int);
218 fl_xyline(int, int, int);
219 fl_rect()
220 fl_line()
221 img->draw()
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...
226 static Fl_Offscreen fl_help_view_buffer;
227 int Fl_Help_View::selection_first = 0;
228 int Fl_Help_View::selection_last = 0;
229 int Fl_Help_View::selection_push_first = 0;
230 int Fl_Help_View::selection_push_last = 0;
231 int Fl_Help_View::selection_drag_first = 0;
232 int Fl_Help_View::selection_drag_last = 0;
233 int Fl_Help_View::selected = 0;
234 int Fl_Help_View::draw_mode = 0;
235 int Fl_Help_View::mouse_x = 0;
236 int Fl_Help_View::mouse_y = 0;
237 int Fl_Help_View::current_pos = 0;
238 Fl_Help_View *Fl_Help_View::current_view = 0L;
239 Fl_Color Fl_Help_View::hv_selection_color;
240 Fl_Color Fl_Help_View::hv_selection_text_color;
243 * Limitation: if a word contains &code; notations, we will calculate a wrong length.
245 * This function must be optimized for speed!
247 void Fl_Help_View::hv_draw(const char *t, int x, int y)
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);
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;
281 /** Adds a text block to the list. */
282 Fl_Help_Block * // O - Pointer to new block
283 Fl_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?
290 Fl_Help_Block *temp; // New block
293 // printf("add_block(s = %p, xx = %d, yy = %d, ww = %d, hh = %d, border = %d)\n",
294 // s, xx, yy, ww, hh, border);
296 if (nblocks_ >= ablocks_)
298 ablocks_ += 16;
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_);
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_ ++;
318 return (temp);
322 /** Adds a new link to the list. */
323 void 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
329 Fl_Help_Link *temp; // New link
330 char *target; // Pointer to target name
333 if (nlinks_ >= alinks_)
335 alinks_ += 16;
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_);
343 temp = links_ + nlinks_;
345 temp->x = xx;
346 temp->y = yy;
347 temp->w = xx + ww;
348 temp->h = yy + hh;
350 strlcpy(temp->filename, n, sizeof(temp->filename));
352 if ((target = strrchr(temp->filename, '#')) != NULL)
354 *target++ = '\0';
355 strlcpy(temp->name, target, sizeof(temp->name));
357 else
358 temp->name[0] = '\0';
360 nlinks_ ++;
364 /** Adds a new target to the list. */
365 void Fl_Help_View::add_target(const char *n, // I - Name of target
366 int yy) // I - Y position of target
368 Fl_Help_Target *temp; // New target
371 if (ntargets_ >= atargets_)
373 atargets_ += 16;
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_);
381 temp = targets_ + ntargets_;
383 temp->y = yy;
384 strlcpy(temp->name, n, sizeof(temp->name));
386 ntargets_ ++;
389 /** Compares two targets.*/
390 int // O - Result of comparison
391 Fl_Help_View::compare_targets(const Fl_Help_Target *t0, // I - First target
392 const Fl_Help_Target *t1) // I - Second target
394 return (strcasecmp(t0->name, t1->name));
397 /** Computes the alignment for a line in a block.*/
398 int // O - New line
399 Fl_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
405 int offset; // Alignment offset
408 switch (a)
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;
421 block->line[line] = block->x + offset;
423 if (line < 31)
424 line ++;
426 while (l < nlinks_)
428 links_[l].x += offset;
429 links_[l].w += offset;
430 l ++;
433 return (line);
436 /** Draws the Fl_Help_View widget. */
437 void
438 Fl_Help_View::draw()
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
459 // Draw the scrollbar(s) and box first...
460 ww = w();
461 hh = h();
462 i = 0;
464 draw_box(b, x(), y(), ww, hh, bgcolor_);
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();
478 draw_child(hscrollbar_);
479 hh -= scrollsize;
481 if ( ver_vis ) {
482 if ( scrollbar_.w() != scrollsize ) { // scrollsize changed?
483 scrollbar_.resize(scorn_x, y(), scrollsize, scorn_y - y());
484 init_sizes();
486 draw_child(scrollbar_);
487 ww -= scrollsize;
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);
496 if (!value_)
497 return;
499 if (current_view == this && selected) {
500 hv_selection_color = FL_SELECTION_COLOR;
501 hv_selection_text_color = fl_contrast(textcolor_, FL_SELECTION_COLOR);
503 current_pos = 0;
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_);
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()))
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;
523 initfont(font, fsize, fcolor);
525 for (ptr = block->start, s = buf; ptr < block->end;)
527 if ((*ptr == '<' || isspace((*ptr)&255)) && s > buf)
529 if (!head && !pre)
531 // Check width...
532 *s = '\0';
533 s = buf;
534 ww = (int)fl_width(buf);
536 if (needspace && xx > block->x)
537 xx += (int)fl_width(' ');
539 if ((xx + ww) > block->w)
541 if (line < 31)
542 line ++;
543 xx = block->line[line];
544 yy += hh;
545 hh = 0;
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);
554 current_pos = ptr-value_;
556 xx += ww;
557 if ((fsize + 2) > hh)
558 hh = fsize + 2;
560 needspace = 0;
562 else if (pre)
564 while (isspace((*ptr)&255))
566 if (*ptr == '\n')
568 *s = '\0';
569 s = buf;
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));
576 current_pos = ptr-value_;
577 if (line < 31)
578 line ++;
579 xx = block->line[line];
580 yy += hh;
581 hh = fsize + 2;
583 else if (*ptr == '\t')
585 // Do tabs every 8 columns...
586 while (((s - buf) & 7))
587 *s++ = ' ';
589 else
590 *s++ = ' ';
592 if ((fsize + 2) > hh)
593 hh = fsize + 2;
595 ptr ++;
598 if (s > buf)
600 *s = '\0';
601 s = buf;
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_;
611 needspace = 0;
613 else
615 s = buf;
617 while (isspace((*ptr)&255))
618 ptr ++;
619 current_pos = ptr-value_;
623 if (*ptr == '<')
625 ptr ++;
627 if (strncmp(ptr, "!--", 3) == 0)
629 // Comment...
630 ptr += 3;
631 if ((ptr = strstr(ptr, "-->")) != NULL)
633 ptr += 3;
634 continue;
636 else
637 break;
640 while (*ptr && *ptr != '>' && !isspace((*ptr)&255))
641 if (s < (buf + sizeof(buf) - 1))
642 *s++ = *ptr++;
643 else
644 ptr ++;
646 *s = '\0';
647 s = buf;
649 attrs = ptr;
650 while (*ptr && *ptr != '>')
651 ptr ++;
653 if (*ptr == '>')
654 ptr ++;
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)
662 if (line < 31)
663 line ++;
664 xx = block->line[line];
665 yy += hh;
666 hh = 0;
668 else if (strcasecmp(buf, "HR") == 0)
670 fl_line(block->x + x(), yy + y(), block->w + x(),
671 yy + y());
673 if (line < 31)
674 line ++;
675 xx = block->line[line];
676 yy += 2 * hh;
677 hh = 0;
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)
695 if (tolower(buf[0]) == 'h')
697 font = FL_HELVETICA_BOLD;
698 fsize = textsize_ + '7' - buf[1];
700 else if (strcasecmp(buf, "DT") == 0)
702 font = textfont_ | FL_ITALIC;
703 fsize = textsize_;
705 else if (strcasecmp(buf, "PRE") == 0)
707 font = FL_COURIER;
708 fsize = textsize_;
709 pre = 1;
712 if (strcasecmp(buf, "LI") == 0)
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());
723 pushfont(font, fsize);
725 else if (strcasecmp(buf, "A") == 0 &&
726 get_attr(attrs, "HREF", attr, sizeof(attr)) != NULL)
728 fl_color(linkcolor_);
729 underline = 1;
731 else if (strcasecmp(buf, "/A") == 0)
733 fl_color(textcolor_);
734 underline = 0;
736 else if (strcasecmp(buf, "FONT") == 0)
738 if (get_attr(attrs, "COLOR", attr, sizeof(attr)) != NULL) {
739 textcolor_ = get_color(attr, textcolor_);
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;
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));
762 pushfont(font, fsize);
764 else if (strcasecmp(buf, "/FONT") == 0)
766 popfont(font, fsize, textcolor_);
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)
778 int tx, ty, tw, th;
780 if (tolower(buf[1]) == 'h')
781 pushfont(font |= FL_BOLD, fsize);
782 else
783 pushfont(font = textfont_, fsize);
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;
790 if (tx < 0)
792 tw += tx;
793 tx = 0;
796 if (ty < 0)
798 th += ty;
799 ty = 0;
802 tx += x();
803 ty += y();
805 if (block->bgcolor != bgcolor_)
807 fl_color(block->bgcolor);
808 fl_rectf(tx, ty, tw, th);
809 fl_color(textcolor_);
812 if (block->border)
813 fl_rect(tx, ty, tw, th);
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)
844 popfont(font, fsize, fcolor);
845 pre = 0;
847 else if (strcasecmp(buf, "IMG") == 0)
849 Fl_Shared_Image *img = 0;
850 int width, height;
851 char wattr[8], hattr[8];
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);
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();
865 if (!width || !height) {
866 if (get_attr(attrs, "ALT", attr, sizeof(attr)) == NULL) {
867 strcpy(attr, "IMG");
871 ww = width;
873 if (needspace && xx > block->x)
874 xx += (int)fl_width(' ');
876 if ((xx + ww) > block->w)
878 if (line < 31)
879 line ++;
881 xx = block->line[line];
882 yy += hh;
883 hh = 0;
886 if (img) {
887 img->draw(xx + x() - leftline_,
888 yy + y() - fl_height() + fl_descent() + 2);
891 xx += ww;
892 if ((height + 2) > hh)
893 hh = height + 2;
895 needspace = 0;
898 else if (*ptr == '\n' && pre)
900 *s = '\0';
901 s = buf;
903 hv_draw(buf, xx + x() - leftline_, yy + y());
905 if (line < 31)
906 line ++;
907 xx = block->line[line];
908 yy += hh;
909 hh = fsize + 2;
910 needspace = 0;
912 ptr ++;
913 current_pos = ptr-value_;
915 else if (isspace((*ptr)&255))
917 if (pre)
919 if (*ptr == ' ')
920 *s++ = ' ';
921 else
923 // Do tabs every 8 columns...
924 while (((s - buf) & 7))
925 *s++ = ' ';
929 ptr ++;
930 if (!pre) current_pos = ptr-value_;
931 needspace = 1;
933 else if (*ptr == '&')
935 ptr ++;
937 int qch = quote_char(ptr);
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;
949 if ((fsize + 2) > hh)
950 hh = fsize + 2;
952 else
954 *s++ = *ptr++;
956 if ((fsize + 2) > hh)
957 hh = fsize + 2;
961 *s = '\0';
963 if (s > buf && !pre && !head)
965 ww = (int)fl_width(buf);
967 if (needspace && xx > block->x)
968 xx += (int)fl_width(' ');
970 if ((xx + ww) > block->w)
972 if (line < 31)
973 line ++;
974 xx = block->line[line];
975 yy += hh;
976 hh = 0;
980 if (s > buf && !head)
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_;
989 fl_pop_clip();
994 /** Finds the specified string \p s at starting position \p p.
996 \return the matching position or -1 if not found
998 int // O - Matching position or -1 if not found
999 Fl_Help_View::find(const char *s, // I - String to find
1000 int p) // I - Starting position
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
1010 // Range check input and value...
1011 if (!s || !value_) return -1;
1013 if (p < 0 || p >= (int)strlen(value_)) p = 0;
1014 else if (p > 0) p ++;
1016 // Look for the string...
1017 for (i = nblocks_, b = blocks_; i > 0; i --, b ++) {
1018 if (b->end < (value_ + p))
1019 continue;
1021 if (b->start < (value_ + p)) bp = value_ + p;
1022 else bp = b->start;
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;
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;
1044 if (!*sp) {
1045 // Found a match!
1046 topline(b->y - b->h);
1047 return (b->end - value_);
1051 // No match!
1052 return (-1);
1055 /** Formats the help text. */
1056 void 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...
1096 // Reset document width...
1097 int scrollsize = scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size();
1098 hsize_ = w() - scrollsize - Fl::box_dw(b);
1100 done = 0;
1101 while (!done)
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());
1113 tc = rc = bgcolor_;
1115 strcpy(title_, "Untitled");
1117 if (!value_)
1118 return;
1120 // Setup for formatting...
1121 initfont(font, fsize, fcolor);
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;
1141 // Html text character loop
1142 for (ptr = value_, s = buf; *ptr;)
1144 // End of word?
1145 if ((*ptr == '<' || isspace((*ptr)&255)) && s > buf)
1147 // Get width of word parsed so far...
1148 *s = '\0';
1149 ww = (int)fl_width(buf);
1151 if (!head && !pre)
1153 // Check width...
1154 if (ww > hsize_) {
1155 hsize_ = ww;
1156 done = 0;
1157 break;
1160 if (needspace && xx > block->x)
1161 ww += (int)fl_width(' ');
1163 // printf("line = %d, xx = %d, ww = %d, block->x = %d, block->w = %d\n",
1164 // line, xx, ww, block->x, block->w);
1166 if ((xx + ww) > block->w)
1168 line = do_align(block, line, xx, newalign, links);
1169 xx = block->x;
1170 yy += hh;
1171 block->h += hh;
1172 hh = 0;
1175 if (linkdest[0])
1176 add_link(linkdest, xx, yy - fsize, ww, fsize);
1178 xx += ww;
1179 if ((fsize + 2) > hh)
1180 hh = fsize + 2;
1182 needspace = 0;
1184 else if (pre)
1186 // Add a link as needed...
1187 if (linkdest[0])
1188 add_link(linkdest, xx, yy - hh, ww, hh);
1190 xx += ww;
1191 if ((fsize + 2) > hh)
1192 hh = fsize + 2;
1194 // Handle preformatted text...
1195 while (isspace((*ptr)&255))
1197 if (*ptr == '\n')
1199 if (xx > hsize_) break;
1201 line = do_align(block, line, xx, newalign, links);
1202 xx = block->x;
1203 yy += hh;
1204 block->h += hh;
1205 hh = fsize + 2;
1207 else
1208 xx += (int)fl_width(' ');
1210 if ((fsize + 2) > hh)
1211 hh = fsize + 2;
1213 ptr ++;
1216 if (xx > hsize_) {
1217 hsize_ = xx;
1218 done = 0;
1219 break;
1222 needspace = 0;
1224 else
1226 // Handle normal text or stuff in the <HEAD> section...
1227 while (isspace((*ptr)&255))
1228 ptr ++;
1231 s = buf;
1234 if (*ptr == '<')
1236 // Handle html tags..
1237 start = ptr;
1238 ptr ++;
1240 if (strncmp(ptr, "!--", 3) == 0)
1242 // Comment...
1243 ptr += 3;
1244 if ((ptr = strstr(ptr, "-->")) != NULL)
1246 ptr += 3;
1247 continue;
1249 else
1250 break;
1253 while (*ptr && *ptr != '>' && !isspace((*ptr)&255))
1254 if (s < (buf + sizeof(buf) - 1))
1255 *s++ = *ptr++;
1256 else
1257 ptr ++;
1259 *s = '\0';
1260 s = buf;
1262 // puts(buf);
1264 attrs = ptr;
1265 while (*ptr && *ptr != '>')
1266 ptr ++;
1268 if (*ptr == '>')
1269 ptr ++;
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)
1277 // Copy the title in the document...
1278 for (s = title_;
1279 *ptr != '<' && *ptr && s < (title_ + sizeof(title_) - 1);
1280 *s++ = *ptr++);
1282 *s = '\0';
1283 s = buf;
1285 else if (strcasecmp(buf, "A") == 0)
1287 if (get_attr(attrs, "NAME", attr, sizeof(attr)) != NULL)
1288 add_target(attr, yy - fsize - 2);
1290 if (get_attr(attrs, "HREF", attr, sizeof(attr)) != NULL)
1291 strlcpy(linkdest, attr, sizeof(linkdest));
1293 else if (strcasecmp(buf, "/A") == 0)
1294 linkdest[0] = '\0';
1295 else if (strcasecmp(buf, "BODY") == 0)
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()));
1304 else if (strcasecmp(buf, "BR") == 0)
1306 line = do_align(block, line, xx, newalign, links);
1307 xx = block->x;
1308 block->h += hh;
1309 yy += hh;
1310 hh = 0;
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)
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;
1336 if (strcasecmp(buf, "UL") == 0 ||
1337 strcasecmp(buf, "OL") == 0 ||
1338 strcasecmp(buf, "DL") == 0)
1340 block->h += fsize + 2;
1341 xx = margins.push(4 * fsize);
1343 else if (strcasecmp(buf, "TABLE") == 0)
1345 if (get_attr(attrs, "BORDER", attr, sizeof(attr)))
1346 border = (uchar)atoi(attr);
1347 else
1348 border = 0;
1350 tc = rc = get_color(get_attr(attrs, "BGCOLOR", attr, sizeof(attr)), bgcolor_);
1352 block->h += fsize + 2;
1354 format_table(&table_width, columns, start);
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;
1366 switch (get_align(attrs, talign))
1368 default :
1369 table_offset = 0;
1370 break;
1372 case CENTER :
1373 table_offset = (hsize_ - table_width) / 2 - textsize_;
1374 break;
1376 case RIGHT :
1377 table_offset = hsize_ - table_width - textsize_;
1378 break;
1381 column = 0;
1384 if (tolower(buf[0]) == 'h' && isdigit(buf[1]))
1386 font = FL_HELVETICA_BOLD;
1387 fsize = textsize_ + '7' - buf[1];
1389 else if (strcasecmp(buf, "DT") == 0)
1391 font = textfont_ | FL_ITALIC;
1392 fsize = textsize_;
1394 else if (strcasecmp(buf, "PRE") == 0)
1396 font = FL_COURIER;
1397 fsize = textsize_;
1398 pre = 1;
1400 else
1402 font = textfont_;
1403 fsize = textsize_;
1406 pushfont(font, fsize);
1408 yy = block->y + block->h;
1409 hh = 0;
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)
1418 hh += 2 * fsize;
1419 yy += fsize;
1422 if (row)
1423 block = add_block(start, xx, yy, block->w, 0);
1424 else
1425 block = add_block(start, xx, yy, hsize_, 0);
1427 needspace = 0;
1428 line = 0;
1430 if (strcasecmp(buf, "CENTER") == 0)
1431 newalign = talign = CENTER;
1432 else
1433 newalign = get_align(attrs, talign);
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)
1449 line = do_align(block, line, xx, newalign, links);
1450 xx = block->x;
1451 block->end = ptr;
1453 if (strcasecmp(buf, "/UL") == 0 ||
1454 strcasecmp(buf, "/OL") == 0 ||
1455 strcasecmp(buf, "/DL") == 0)
1457 xx = margins.pop();
1458 block->h += fsize + 2;
1460 else if (strcasecmp(buf, "/TABLE") == 0)
1462 block->h += fsize + 2;
1463 xx = margins.current();
1465 else if (strcasecmp(buf, "/PRE") == 0)
1467 pre = 0;
1468 hh = 0;
1470 else if (strcasecmp(buf, "/CENTER") == 0)
1471 talign = LEFT;
1473 popfont(font, fsize, fcolor);
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 ++;
1481 block->h += hh;
1482 yy += hh;
1484 if (tolower(buf[2]) == 'l')
1485 yy += fsize + 2;
1487 if (row)
1488 block = add_block(ptr, xx, yy, block->w, 0);
1489 else
1490 block = add_block(ptr, xx, yy, hsize_, 0);
1492 needspace = 0;
1493 hh = 0;
1494 line = 0;
1495 newalign = talign;
1497 else if (strcasecmp(buf, "TR") == 0)
1499 block->end = start;
1500 line = do_align(block, line, xx, newalign, links);
1501 xx = block->x;
1502 block->h += hh;
1504 if (row)
1506 yy = blocks_[row].y + blocks_[row].h;
1508 for (cell = blocks_ + row + 1; cell <= block; cell ++)
1509 if ((cell->y + cell->h) > yy)
1510 yy = cell->y + cell->h;
1512 block = blocks_ + row;
1514 block->h = yy - block->y + 2;
1516 for (i = 0; i < column; i ++)
1517 if (cells[i])
1519 cell = blocks_ + cells[i];
1520 cell->h = block->h;
1524 memset(cells, 0, sizeof(cells));
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;
1534 rc = get_color(get_attr(attrs, "BGCOLOR", attr, sizeof(attr)), tc);
1536 else if (strcasecmp(buf, "/TR") == 0 && row)
1538 line = do_align(block, line, xx, newalign, links);
1539 block->end = start;
1540 block->h += hh;
1541 talign = LEFT;
1543 xx = blocks_[row].x;
1544 yy = blocks_[row].y + blocks_[row].h;
1546 for (cell = blocks_ + row + 1; cell <= block; cell ++)
1547 if ((cell->y + cell->h) > yy)
1548 yy = cell->y + cell->h;
1550 block = blocks_ + row;
1552 block->h = yy - block->y + 2;
1554 for (i = 0; i < column; i ++)
1555 if (cells[i])
1557 cell = blocks_ + cells[i];
1558 cell->h = block->h;
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;
1567 else if ((strcasecmp(buf, "TD") == 0 ||
1568 strcasecmp(buf, "TH") == 0) && row)
1570 int colspan; // COLSPAN attribute
1573 line = do_align(block, line, xx, newalign, links);
1574 block->end = start;
1575 block->h += hh;
1577 if (strcasecmp(buf, "TH") == 0)
1578 font = textfont_ | FL_BOLD;
1579 else
1580 font = textfont_;
1582 fsize = textsize_;
1584 xx = blocks_[row].x + fsize + 3 + table_offset;
1585 for (i = 0; i < column; i ++)
1586 xx += columns[i] + 6;
1588 margins.push(xx - margins.current());
1590 if (get_attr(attrs, "COLSPAN", attr, sizeof(attr)) != NULL)
1591 colspan = atoi(attr);
1592 else
1593 colspan = 1;
1595 for (i = 0, ww = -6; i < colspan; i ++)
1596 ww += columns[column + i] + 6;
1598 if (block->end == block->start && nblocks_ > 1)
1600 nblocks_ --;
1601 block --;
1604 pushfont(font, fsize);
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;
1614 cells[column] = block - blocks_;
1616 column += colspan;
1618 block->bgcolor = get_color(get_attr(attrs, "BGCOLOR", attr,
1619 sizeof(attr)), rc);
1621 else if ((strcasecmp(buf, "/TD") == 0 ||
1622 strcasecmp(buf, "/TH") == 0) && row)
1624 line = do_align(block, line, xx, newalign, links);
1625 popfont(font, fsize, fcolor);
1626 xx = margins.pop();
1627 talign = LEFT;
1629 else if (strcasecmp(buf, "FONT") == 0)
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;
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)));
1651 pushfont(font, fsize);
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)
1679 Fl_Shared_Image *img = 0;
1680 int width;
1681 int height;
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);
1689 if (get_attr(attrs, "SRC", attr, sizeof(attr))) {
1690 img = get_image(attr, width, height);
1691 width = img->w();
1692 height = img->h();
1695 ww = width;
1697 if (ww > hsize_) {
1698 hsize_ = ww;
1699 done = 0;
1700 break;
1703 if (needspace && xx > block->x)
1704 ww += (int)fl_width(' ');
1706 if ((xx + ww) > block->w)
1708 line = do_align(block, line, xx, newalign, links);
1709 xx = block->x;
1710 yy += hh;
1711 block->h += hh;
1712 hh = 0;
1715 if (linkdest[0])
1716 add_link(linkdest, xx, yy - height, ww, height);
1718 xx += ww;
1719 if ((height + 2) > hh)
1720 hh = height + 2;
1722 needspace = 0;
1725 else if (*ptr == '\n' && pre)
1727 if (linkdest[0])
1728 add_link(linkdest, xx, yy - hh, ww, hh);
1730 if (xx > hsize_) {
1731 hsize_ = xx;
1732 done = 0;
1733 break;
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 ++;
1743 else if (isspace((*ptr)&255))
1745 needspace = 1;
1746 if ( pre ) {
1747 xx += (int)fl_width(' ');
1749 ptr ++;
1751 else if (*ptr == '&' && s < (buf + sizeof(buf) - 1))
1753 // Handle html '&' codes, eg. "&amp;"
1754 ptr ++;
1756 int qch = quote_char(ptr);
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;
1768 if ((fsize + 2) > hh)
1769 hh = fsize + 2;
1771 else
1773 if (s < (buf + sizeof(buf) - 1))
1774 *s++ = *ptr++;
1775 else
1776 ptr ++;
1778 if ((fsize + 2) > hh)
1779 hh = fsize + 2;
1783 if (s > buf && !head)
1785 *s = '\0';
1786 ww = (int)fl_width(buf);
1788 // printf("line = %d, xx = %d, ww = %d, block->x = %d, block->w = %d\n",
1789 // line, xx, ww, block->x, block->w);
1791 if (ww > hsize_) {
1792 hsize_ = ww;
1793 done = 0;
1794 break;
1797 if (needspace && xx > block->x)
1798 ww += (int)fl_width(' ');
1800 if ((xx + ww) > block->w)
1802 line = do_align(block, line, xx, newalign, links);
1803 xx = block->x;
1804 yy += hh;
1805 block->h += hh;
1806 hh = 0;
1809 if (linkdest[0])
1810 add_link(linkdest, xx, yy - fsize, ww, fsize);
1812 xx += ww;
1815 do_align(block, line, xx, newalign, links);
1817 block->end = ptr;
1818 size_ = yy + hh;
1821 // printf("margins.depth_=%d\n", margins.depth_);
1823 if (ntargets_ > 1)
1824 qsort(targets_, ntargets_, sizeof(Fl_Help_Target),
1825 (compare_func_t)compare_targets);
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);
1833 if (hsize_ > (w() - dw)) {
1834 hscrollbar_.show();
1836 dh += ss;
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);
1849 } else {
1850 hscrollbar_.hide();
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();
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);
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);
1876 /** Formats a table */
1877 void
1878 Fl_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
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
1904 // Clear widths...
1905 *table_width = 0;
1906 for (column = 0; column < MAX_COLUMNS; column ++)
1908 columns[column] = 0;
1909 minwidths[column] = 0;
1912 num_columns = 0;
1913 colspan = 0;
1914 max_width = 0;
1915 pre = 0;
1916 needspace = 0;
1917 fstack_.top(font, fsize, fcolor);
1919 // Scan the table...
1920 for (ptr = table, column = -1, width = 0, s = buf, incell = 0; *ptr;)
1922 if ((*ptr == '<' || isspace((*ptr)&255)) && s > buf && incell)
1924 // Check width...
1925 if (needspace)
1927 *s++ = ' ';
1928 needspace = 0;
1931 *s = '\0';
1932 temp_width = (int)fl_width(buf);
1933 s = buf;
1935 if (temp_width > minwidths[column])
1936 minwidths[column] = temp_width;
1938 width += temp_width;
1940 if (width > max_width)
1941 max_width = width;
1944 if (*ptr == '<')
1946 start = ptr;
1948 for (s = buf, ptr ++; *ptr && *ptr != '>' && !isspace((*ptr)&255);)
1949 if (s < (buf + sizeof(buf) - 1))
1950 *s++ = *ptr++;
1951 else
1952 ptr ++;
1954 *s = '\0';
1955 s = buf;
1957 attrs = ptr;
1958 while (*ptr && *ptr != '>')
1959 ptr ++;
1961 if (*ptr == '>')
1962 ptr ++;
1964 if (strcasecmp(buf, "BR") == 0 ||
1965 strcasecmp(buf, "HR") == 0)
1967 width = 0;
1968 needspace = 0;
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)
1988 width = 0;
1989 needspace = 0;
1991 if (tolower(buf[0]) == 'h' && isdigit(buf[1]))
1993 font = FL_HELVETICA_BOLD;
1994 fsize = textsize_ + '7' - buf[1];
1996 else if (strcasecmp(buf, "DT") == 0)
1998 font = textfont_ | FL_ITALIC;
1999 fsize = textsize_;
2001 else if (strcasecmp(buf, "PRE") == 0)
2003 font = FL_COURIER;
2004 fsize = textsize_;
2005 pre = 1;
2007 else if (strcasecmp(buf, "LI") == 0)
2009 width += 4 * fsize;
2010 font = textfont_;
2011 fsize = textsize_;
2013 else
2015 font = textfont_;
2016 fsize = textsize_;
2019 pushfont(font, fsize);
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)
2034 width = 0;
2035 needspace = 0;
2037 popfont(font, fsize, fcolor);
2039 else if (strcasecmp(buf, "TR") == 0 || strcasecmp(buf, "/TR") == 0 ||
2040 strcasecmp(buf, "/TABLE") == 0)
2042 // printf("%s column = %d, colspan = %d, num_columns = %d\n",
2043 // buf, column, colspan, num_columns);
2045 if (column >= 0)
2047 // This is a hack to support COLSPAN...
2048 max_width /= colspan;
2050 while (colspan > 0)
2052 if (max_width > columns[column])
2053 columns[column] = max_width;
2055 column ++;
2056 colspan --;
2060 if (strcasecmp(buf, "/TABLE") == 0)
2061 break;
2063 needspace = 0;
2064 column = -1;
2065 width = 0;
2066 max_width = 0;
2067 incell = 0;
2069 else if (strcasecmp(buf, "TD") == 0 ||
2070 strcasecmp(buf, "TH") == 0)
2072 // printf("BEFORE column = %d, colspan = %d, num_columns = %d\n",
2073 // column, colspan, num_columns);
2075 if (column >= 0)
2077 // This is a hack to support COLSPAN...
2078 max_width /= colspan;
2080 while (colspan > 0)
2082 if (max_width > columns[column])
2083 columns[column] = max_width;
2085 column ++;
2086 colspan --;
2089 else
2090 column ++;
2092 if (get_attr(attrs, "COLSPAN", attr, sizeof(attr)) != NULL)
2093 colspan = atoi(attr);
2094 else
2095 colspan = 1;
2097 // printf("AFTER column = %d, colspan = %d, num_columns = %d\n",
2098 // column, colspan, num_columns);
2100 if ((column + colspan) >= num_columns)
2101 num_columns = column + colspan;
2103 needspace = 0;
2104 width = 0;
2105 incell = 1;
2107 if (strcasecmp(buf, "TH") == 0)
2108 font = textfont_ | FL_BOLD;
2109 else
2110 font = textfont_;
2112 fsize = textsize_;
2114 pushfont(font, fsize);
2116 if (get_attr(attrs, "WIDTH", attr, sizeof(attr)) != NULL)
2117 max_width = get_length(attr);
2118 else
2119 max_width = 0;
2121 // printf("max_width = %d\n", max_width);
2123 else if (strcasecmp(buf, "/TD") == 0 ||
2124 strcasecmp(buf, "/TH") == 0)
2126 incell = 0;
2127 popfont(font, fsize, fcolor);
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)
2153 Fl_Shared_Image *img = 0;
2154 int iwidth, iheight;
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);
2162 if (get_attr(attrs, "SRC", attr, sizeof(attr))) {
2163 img = get_image(attr, iwidth, iheight);
2164 iwidth = img->w();
2165 iheight = img->h();
2168 if (iwidth > minwidths[column])
2169 minwidths[column] = iwidth;
2171 width += iwidth;
2172 if (needspace)
2173 width += (int)fl_width(' ');
2175 if (width > max_width)
2176 max_width = width;
2178 needspace = 0;
2181 else if (*ptr == '\n' && pre)
2183 width = 0;
2184 needspace = 0;
2185 ptr ++;
2187 else if (isspace((*ptr)&255))
2189 needspace = 1;
2191 ptr ++;
2193 else if (*ptr == '&' && s < (buf + sizeof(buf) - 1))
2195 ptr ++;
2197 int qch = quote_char(ptr);
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;
2210 else
2212 if (s < (buf + sizeof(buf) - 1))
2213 *s++ = *ptr++;
2214 else
2215 ptr ++;
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;
2226 #ifdef DEBUG
2227 printf("num_columns = %d, table_width = %d\n", num_columns, *table_width);
2228 #endif // DEBUG
2230 if (num_columns == 0)
2231 return;
2233 // Add up the widths...
2234 for (column = 0, width = 0; column < num_columns; column ++)
2235 width += columns[column];
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
2244 // Adjust the width if needed...
2245 int scale_width = *table_width;
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;
2253 if (width < scale_width) {
2254 #ifdef DEBUG
2255 printf("Scaling table up to %d from %d...\n", scale_width, width);
2256 #endif // DEBUG
2258 *table_width = 0;
2260 scale_width = (scale_width - width) / num_columns;
2262 #ifdef DEBUG
2263 printf("adjusted scale_width = %d\n", scale_width);
2264 #endif // DEBUG
2266 for (column = 0; column < num_columns; column ++) {
2267 columns[column] += scale_width;
2269 (*table_width) += columns[column];
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
2277 for (column = 0; column < num_columns; column ++) {
2278 width -= minwidths[column];
2279 scale_width -= minwidths[column];
2282 #ifdef DEBUG
2283 printf("adjusted width = %d, scale_width = %d\n", width, scale_width);
2284 #endif // DEBUG
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];
2294 *table_width = 0;
2295 for (column = 0; column < num_columns; column ++) {
2296 (*table_width) += columns[column];
2299 else if (*table_width == 0)
2300 *table_width = width;
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
2310 /** Frees memory used for the document. */
2311 void
2312 Fl_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
2323 for (ptr = value_; *ptr;)
2325 if (*ptr == '<')
2327 ptr ++;
2329 if (strncmp(ptr, "!--", 3) == 0)
2331 // Comment...
2332 ptr += 3;
2333 if ((ptr = strstr(ptr, "-->")) != NULL)
2335 ptr += 3;
2336 continue;
2338 else
2339 break;
2342 s = buf;
2344 while (*ptr && *ptr != '>' && !isspace((*ptr)&255))
2345 if (s < (buf + sizeof(buf) - 1))
2346 *s++ = *ptr++;
2347 else
2348 ptr ++;
2350 *s = '\0';
2352 attrs = ptr;
2353 while (*ptr && *ptr != '>')
2354 ptr ++;
2356 if (*ptr == '>')
2357 ptr ++;
2359 if (strcasecmp(buf, "IMG") == 0)
2361 Fl_Shared_Image *img;
2362 int width;
2363 int height;
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);
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();
2379 else
2380 ptr ++;
2383 free((void *)value_);
2384 value_ = 0;
2387 // Free all of the arrays...
2388 if (nblocks_) {
2389 free(blocks_);
2391 ablocks_ = 0;
2392 nblocks_ = 0;
2393 blocks_ = 0;
2396 if (nlinks_) {
2397 free(links_);
2399 alinks_ = 0;
2400 nlinks_ = 0;
2401 links_ = 0;
2404 if (ntargets_) {
2405 free(targets_);
2407 atargets_ = 0;
2408 ntargets_ = 0;
2409 targets_ = 0;
2413 /** Gets an alignment attribute. */
2414 int // O - Alignment
2415 Fl_Help_View::get_align(const char *p, // I - Pointer to start of attrs
2416 int a) // I - Default alignment
2418 char buf[255]; // Alignment value
2421 if (get_attr(p, "ALIGN", buf, sizeof(buf)) == NULL)
2422 return (a);
2424 if (strcasecmp(buf, "CENTER") == 0)
2425 return (CENTER);
2426 else if (strcasecmp(buf, "RIGHT") == 0)
2427 return (RIGHT);
2428 else
2429 return (LEFT);
2433 /** Gets an attribute value from the string. */
2434 const char * // O - Pointer to buf or NULL
2435 Fl_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
2440 char name[255], // Name from string
2441 *ptr, // Pointer into name or value
2442 quote; // Quote
2445 buf[0] = '\0';
2447 while (*p && *p != '>')
2449 while (isspace((*p)&255))
2450 p ++;
2452 if (*p == '>' || !*p)
2453 return (NULL);
2455 for (ptr = name; *p && !isspace((*p)&255) && *p != '=' && *p != '>';)
2456 if (ptr < (name + sizeof(name) - 1))
2457 *ptr++ = *p++;
2458 else
2459 p ++;
2461 *ptr = '\0';
2463 if (isspace((*p)&255) || !*p || *p == '>')
2464 buf[0] = '\0';
2465 else
2467 if (*p == '=')
2468 p ++;
2470 for (ptr = buf; *p && !isspace((*p)&255) && *p != '>';)
2471 if (*p == '\'' || *p == '\"')
2473 quote = *p++;
2475 while (*p && *p != quote)
2476 if ((ptr - buf + 1) < bufsize)
2477 *ptr++ = *p++;
2478 else
2479 p ++;
2481 if (*p == quote)
2482 p ++;
2484 else if ((ptr - buf + 1) < bufsize)
2485 *ptr++ = *p++;
2486 else
2487 p ++;
2489 *ptr = '\0';
2492 if (strcasecmp(n, name) == 0)
2493 return (buf);
2494 else
2495 buf[0] = '\0';
2497 if (*p == '>')
2498 return (NULL);
2501 return (NULL);
2505 /** Gets a color attribute. */
2506 Fl_Color // O - Color value
2507 Fl_Help_View::get_color(const char *n, // I - Color name
2508 Fl_Color c) // I - Default color value
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 }
2538 if (!n || !n[0]) return c;
2540 if (n[0] == '#') {
2541 // Do hex color lookup
2542 rgb = strtol(n + 1, NULL, 16);
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;
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);
2559 return c;
2564 /** Gets an inline image.
2566 The image reference count is maintained accordingly, such that
2567 the image can be released exactly once when the document is closed.
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).
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).
2577 /* Implementation note: (A.S. Apr 05, 2009)
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().
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 !
2587 This should be fixed in FLTK 1.3 !
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.
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().
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.
2602 Each image must be released exactly once in the destructor or before
2603 a new document is loaded: see free_data().
2606 Fl_Shared_Image *
2607 Fl_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...
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));
2619 if ((tempptr = strrchr(strchr(directory_, ':') + 3, '/')) != NULL) {
2620 strlcpy(tempptr, name, sizeof(temp) - (tempptr - temp));
2621 } else {
2622 strlcat(temp, name, sizeof(temp));
2624 } else {
2625 snprintf(temp, sizeof(temp), "%s/%s", directory_, name);
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);
2637 if (link_) localname = (*link_)(this, temp);
2638 else localname = temp;
2639 } else if (link_) localname = (*link_)(this, name);
2640 else localname = name;
2642 if (!localname) return 0;
2644 if (strncmp(localname, "file:", 5) == 0) localname += 5;
2646 if (initial_load) {
2647 if ((ip = Fl_Shared_Image::get(localname, W, H)) == NULL) {
2648 ip = (Fl_Shared_Image *)&broken_image;
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();
2658 return ip;
2662 /** Gets a length value, either absolute or %. */
2664 Fl_Help_View::get_length(const char *l) { // I - Value
2665 int val; // Integer value
2667 if (!l[0]) return 0;
2669 val = atoi(l);
2670 if (l[strlen(l) - 1] == '%') {
2671 if (val > 100) val = 100;
2672 else if (val < 0) val = 0;
2674 int scrollsize = scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size();
2675 val = val * (hsize_ - scrollsize) / 100;
2678 return val;
2682 Fl_Help_Link *Fl_Help_View::find_link(int xx, int yy)
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;
2691 return i ? linkp : 0L;
2694 void Fl_Help_View::follow_link(Fl_Help_Link *linkp)
2696 char target[32]; // Current target
2698 clear_selection();
2700 strlcpy(target, linkp->name, sizeof(target));
2702 set_changed();
2704 if (strcmp(linkp->filename, filename_) != 0 && linkp->filename[0])
2706 char dir[FL_PATH_MAX]; // Current directory
2707 char temp[FL_PATH_MAX], // Temporary filename
2708 *tempptr; // Pointer into temporary filename
2711 if (strchr(directory_, ':') != NULL &&
2712 strchr(linkp->filename, ':') == NULL)
2714 if (linkp->filename[0] == '/')
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));
2722 else
2723 snprintf(temp, sizeof(temp), "%s/%s", directory_, linkp->filename);
2725 else if (linkp->filename[0] != '/' && strchr(linkp->filename, ':') == NULL)
2727 if (directory_[0])
2728 snprintf(temp, sizeof(temp), "%s/%s", directory_, linkp->filename);
2729 else
2731 fl_getcwd(dir, sizeof(dir));
2732 snprintf(temp, sizeof(temp), "file:%s/%s", dir, linkp->filename);
2735 else
2736 strlcpy(temp, linkp->filename, sizeof(temp));
2738 if (linkp->name[0])
2739 snprintf(temp + strlen(temp), sizeof(temp) - strlen(temp), "#%s",
2740 linkp->name);
2742 load(temp);
2744 else if (target[0])
2745 topline(target);
2746 else
2747 topline(0);
2749 leftline(0);
2752 /** Removes the current text selection. */
2753 void Fl_Help_View::clear_selection()
2755 if (current_view==this)
2756 clear_global_selection();
2758 /** Selects all the text in the view. */
2759 void Fl_Help_View::select_all()
2761 clear_global_selection();
2762 if (!value_) return;
2763 current_view = this;
2764 selection_drag_last = selection_last = strlen(value_);
2765 selected = 1;
2768 void Fl_Help_View::clear_global_selection()
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;
2777 char Fl_Help_View::begin_selection()
2779 clear_global_selection();
2781 if (!fl_help_view_buffer) fl_help_view_buffer = fl_create_offscreen(1, 1);
2783 mouse_x = Fl::event_x();
2784 mouse_y = Fl::event_y();
2785 draw_mode = 1;
2787 current_view = this;
2788 fl_begin_offscreen(fl_help_view_buffer);
2789 draw();
2790 fl_end_offscreen();
2792 draw_mode = 0;
2794 if (selection_push_last) return 1;
2795 else return 0;
2798 char Fl_Help_View::extend_selection()
2800 if (Fl::event_is_click())
2801 return 0;
2803 // printf("old selection_first=%d, selection_last=%d\n",
2804 // selection_first, selection_last);
2806 int sf = selection_first, sl = selection_last;
2808 selected = 1;
2809 mouse_x = Fl::event_x();
2810 mouse_y = Fl::event_y();
2811 draw_mode = 2;
2813 fl_begin_offscreen(fl_help_view_buffer);
2814 draw();
2815 fl_end_offscreen();
2817 draw_mode = 0;
2819 if (selection_push_first < selection_drag_first) {
2820 selection_first = selection_push_first;
2821 } else {
2822 selection_first = selection_drag_first;
2825 if (selection_push_last > selection_drag_last) {
2826 selection_last = selection_push_last;
2827 } else {
2828 selection_last = selection_drag_last;
2831 // printf("new selection_first=%d, selection_last=%d\n",
2832 // selection_first, selection_last);
2834 if (sf!=selection_first || sl!=selection_last) {
2835 // puts("REDRAW!!!\n");
2836 return 1;
2837 } else {
2838 // puts("");
2839 return 0;
2843 // convert a command with up to four letters into an unsigned int
2844 static unsigned int command(const char *cmd)
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;
2861 #define CMD(a, b, c, d) ((a<<24)|(b<<16)|(c<<8)|d)
2863 void Fl_Help_View::end_selection(int clipboard)
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;
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;
2910 int n = s-value_;
2911 if (src && n>selection_first && n<=selection_last) {
2912 while (*src) {
2913 *d++ = *src++;
2915 c = src[-1];
2916 p = isspace(c&255) ? ' ' : c;
2918 continue;
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;
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;
2938 *d = 0;
2939 Fl::copy(txt, strlen(txt), clipboard);
2940 free(txt);
2943 #define ctrl(x) ((x)&0x1f)
2945 /** Handles events in the widget. */
2946 int // O - 1 if we handled it, 0 otherwise
2947 Fl_Help_View::handle(int event) // I - Event to handle
2949 static Fl_Help_Link *linkp; // currently clicked link
2951 int xx = Fl::event_x() - x() + leftline_;
2952 int yy = Fl::event_y() - y() + topline_;
2954 switch (event)
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;
2980 if (begin_selection()) {
2981 fl_cursor(FL_CURSOR_INSERT);
2982 return 1;
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
2993 return 1;
2995 if (current_view==this && selection_push_last) {
2996 if (extend_selection()) redraw();
2997 fl_cursor(FL_CURSOR_INSERT);
2998 return 1;
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);
3007 fl_cursor(FL_CURSOR_DEFAULT);
3008 linkp = 0;
3009 return 1;
3011 if (current_view==this && selection_push_last) {
3012 end_selection();
3013 return 1;
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;
3023 break; }
3025 return (Fl_Group::handle(event));
3028 /**
3029 The constructor creates the Fl_Help_View widget at the specified
3030 position and size.
3032 Fl_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())
3043 color(FL_BACKGROUND2_COLOR, FL_SELECTION_COLOR);
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;
3054 ablocks_ = 0;
3055 nblocks_ = 0;
3056 blocks_ = (Fl_Help_Block *)0;
3058 link_ = (Fl_Help_Func *)0;
3060 alinks_ = 0;
3061 nlinks_ = 0;
3062 links_ = (Fl_Help_Link *)0;
3064 atargets_ = 0;
3065 ntargets_ = 0;
3066 targets_ = (Fl_Help_Target *)0;
3068 directory_[0] = '\0';
3069 filename_[0] = '\0';
3071 topline_ = 0;
3072 leftline_ = 0;
3073 size_ = 0;
3074 hsize_ = 0;
3075 scrollbar_size_ = 0;
3077 scrollbar_.value(0, hh, 0, 1);
3078 scrollbar_.step(8.0);
3079 scrollbar_.show();
3080 scrollbar_.callback(scrollbar_callback);
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();
3089 resize(xx, yy, ww, hh);
3093 /** Destroys the Fl_Help_View widget.
3095 The destructor destroys the widget and frees all memory that has been
3096 allocated for the current document.
3098 Fl_Help_View::~Fl_Help_View()
3100 clear_selection();
3101 free_data();
3105 /** Loads the specified file.
3107 This method loads the specified file or URL.
3109 int // O - 0 on success, -1 on error
3110 Fl_Help_View::load(const char *f)// I - Filename to load (may also have target)
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
3120 // printf("load(%s)\n",f); fflush(stdout);
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();
3132 strlcpy(newname, f, sizeof(newname));
3133 if ((target = strrchr(newname, '#')) != NULL)
3134 *target++ = '\0';
3136 if (link_)
3137 localname = (*link_)(this, newname);
3138 else
3139 localname = filename_;
3141 if (!localname)
3142 return (0);
3144 free_data();
3146 strlcpy(filename_, newname, sizeof(filename_));
3147 strlcpy(directory_, newname, sizeof(directory_));
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';
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);
3165 return(0);
3168 clear_selection();
3170 strlcpy(newname, f, sizeof(newname));
3171 if ((target = strrchr(newname, '#')) != NULL)
3172 *target++ = '\0';
3174 if (link_)
3175 localname = (*link_)(this, newname);
3176 else
3177 localname = filename_;
3179 if (!localname)
3180 return (0);
3182 free_data();
3184 strlcpy(filename_, newname, sizeof(filename_));
3185 strlcpy(directory_, newname, sizeof(directory_));
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';
3194 if (strncmp(localname, "file:", 5) == 0)
3195 localname += 5; // Adjust for local filename...
3197 if ((fp = fl_fopen(localname, "rb")) != NULL)
3199 fseek(fp, 0, SEEK_END);
3200 len = ftell(fp);
3201 rewind(fp);
3203 value_ = (const char *)calloc(len + 1, 1);
3204 if (fread((void *)value_, 1, len, fp)==0) { /* use default 0 */ }
3205 fclose(fp);
3207 else
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);
3218 initial_load = 1;
3219 format();
3220 initial_load = 0;
3222 if (target)
3223 topline(target);
3224 else
3225 topline(0);
3227 return (0);
3231 /** Resizes the help widget. */
3233 void
3234 Fl_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
3239 Fl_Boxtype b = box() ? box() : FL_DOWN_BOX;
3240 // Box to draw...
3243 Fl_Widget::resize(xx, yy, ww, hh);
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);
3252 format();
3256 /** Scrolls the text to the indicated position, given a named destination.
3258 \param[in] n target name
3260 void
3261 Fl_Help_View::topline(const char *n) // I - Target name
3263 Fl_Help_Target key, // Target name key
3264 *target; // Pointer to matching target
3267 if (ntargets_ == 0)
3268 return;
3270 strlcpy(key.name, n, sizeof(key.name));
3272 target = (Fl_Help_Target *)bsearch(&key, targets_, ntargets_, sizeof(Fl_Help_Target),
3273 (compare_func_t)compare_targets);
3275 if (target != NULL)
3276 topline(target->y);
3280 /** Scrolls the text to the indicated position, given a pixel line.
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.
3285 \param[in] top top line number in pixels (0 = start of document)
3287 void
3288 Fl_Help_View::topline(int top) // I - Top line number
3290 if (!value_)
3291 return;
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_;
3299 topline_ = top;
3301 scrollbar_.value(topline_, h() - scrollsize, 0, size_);
3303 do_callback();
3305 redraw();
3309 /** Scrolls the text to the indicated position, given a pixel column.
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.
3314 \param[in] left left column number in pixels (0 = left side)
3316 void
3317 Fl_Help_View::leftline(int left) // I - Left position
3319 if (!value_)
3320 return;
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_;
3328 leftline_ = left;
3330 hscrollbar_.value(leftline_, w() - scrollsize, 0, hsize_);
3332 redraw();
3336 /** Sets the current help text buffer to the string provided and reformats the text.
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.
3341 If \p val is NULL, then the widget is cleared.
3343 void
3344 Fl_Help_View::value(const char *val) // I - Text to view
3346 clear_selection();
3347 free_data();
3348 set_changed();
3350 if (!val)
3351 return;
3353 value_ = strdup(val);
3355 initial_load = 1;
3356 format();
3357 initial_load = 0;
3359 topline(0);
3360 leftline(0);
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
3371 /** Returns the character code associated with a quoted char. */
3372 static int // O - Code or -1 on error
3373 quote_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) }
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);
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;
3497 return -1;
3501 /** The vertical scrollbar callback. */
3502 static void
3503 scrollbar_callback(Fl_Widget *s, void *)
3505 ((Fl_Help_View *)(s->parent()))->topline(int(((Fl_Scrollbar*)s)->value()));
3509 /** The horizontal scrollbar callback. */
3510 static void
3511 hscrollbar_callback(Fl_Widget *s, void *)
3513 ((Fl_Help_View *)(s->parent()))->leftline(int(((Fl_Scrollbar*)s)->value()));
3518 // End of "$Id: Fl_Help_View.cxx 8063 2010-12-19 21:20:10Z matt $".