// // "$Id: Fl_Help_View.cxx 4669 2005-11-28 16:39:57Z matt $" // // Fl_Help_View widget routines. // // Copyright 1997-2005 by Easy Software Products. // Image support donated by Matthias Melcher, Copyright 2000. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 // USA. // // Please report all bugs and problems on the following page: // // http://www.fltk.org/str.php // // Contents: // // Fl_Help_View::add_block() - Add a text block to the list. // Fl_Help_View::add_link() - Add a new link to the list. // Fl_Help_View::add_target() - Add a new target to the list. // Fl_Help_View::compare_targets() - Compare two targets. // Fl_Help_View::do_align() - Compute the alignment for a line in // a block. // Fl_Help_View::draw() - Draw the Fl_Help_View widget. // Fl_Help_View::format() - Format the help text. // Fl_Help_View::format_table() - Format a table... // Fl_Help_View::get_align() - Get an alignment attribute. // Fl_Help_View::get_attr() - Get an attribute value from the string. // Fl_Help_View::get_color() - Get an alignment attribute. // Fl_Help_View::handle() - Handle events in the widget. // Fl_Help_View::Fl_Help_View() - Build a Fl_Help_View widget. // Fl_Help_View::~Fl_Help_View() - Destroy a Fl_Help_View widget. // Fl_Help_View::load() - Load the specified file. // Fl_Help_View::resize() - Resize the help widget. // Fl_Help_View::topline() - Set the top line to the named target. // Fl_Help_View::topline() - Set the top line by number. // Fl_Help_View::value() - Set the help text directly. // scrollbar_callback() - A callback for the scrollbar. // // // Include necessary header files... // #include "Fl_Help_View.h" #include #include #include #include "flstring.h" #include #include #include #ifndef BT_EXTERNAL #include "helpdata.h" #include #endif #if defined(WIN32) && ! defined(__CYGWIN__) # include # include // Visual C++ 2005 incorrectly displays a warning about the use of POSIX APIs // on Windows, which is supposed to be POSIX compliant... # define getcwd _getcwd #else # include #endif // WIN32 #define MAX_COLUMNS 200 // // Typedef the C API sort function type the only way I know how... // extern "C" { typedef int (*compare_func_t)(const void *, const void *); } // // Local functions... // static int quote_char(const char *); static void scrollbar_callback(Fl_Widget *s, void *); static void hscrollbar_callback(Fl_Widget *s, void *); // // Broken image... // static const char *broken_xpm[] = { "16 24 4 1", "@ c #000000", " c #ffffff", "+ c none", "x c #ff0000", // pixels "@@@@@@@+++++++++", "@ @++++++++++", "@ @+++++++++++", "@ @++@++++++++", "@ @@+++++++++", "@ @+++@+++++", "@ @++@@++++@", "@ xxx @@ @++@@", "@ xxx xx@@ @", "@ xxx xxx @", "@ xxxxxx @", "@ xxxx @", "@ xxxxxx @", "@ xxx xxx @", "@ xxx xxx @", "@ xxx xxx @", "@ @", "@ @", "@ @", "@ @", "@ @", "@ @", "@ @", "@@@@@@@@@@@@@@@@", NULL }; static Fl_Pixmap broken_image(broken_xpm); // // 'Fl_Help_View::add_block()' - Add a text block to the list. // Fl_Help_Block * // O - Pointer to new block Fl_Help_View::add_block(const char *s, // I - Pointer to start of block text int xx, // I - X position of block int yy, // I - Y position of block int ww, // I - Right margin of block int hh, // I - Height of block unsigned char border) // I - Draw border? { Fl_Help_Block *temp; // New block // printf("add_block(s = %p, xx = %d, yy = %d, ww = %d, hh = %d, border = %d)\n", // s, xx, yy, ww, hh, border); if (nblocks_ >= ablocks_) { ablocks_ += 16; if (ablocks_ == 16) blocks_ = (Fl_Help_Block *)malloc(sizeof(Fl_Help_Block) * ablocks_); else blocks_ = (Fl_Help_Block *)realloc(blocks_, sizeof(Fl_Help_Block) * ablocks_); } temp = blocks_ + nblocks_; memset(temp, 0, sizeof(Fl_Help_Block)); temp->start = s; temp->end = s; temp->x = xx; temp->y = yy; temp->w = ww; temp->h = hh; temp->border = border; temp->bgcolor = bgcolor_; nblocks_ ++; return (temp); } // // 'Fl_Help_View::add_link()' - Add a new link to the list. // void Fl_Help_View::add_link(const char *n, // I - Name of link int xx, // I - X position of link int yy, // I - Y position of link int ww, // I - Width of link text int hh) // I - Height of link text { Fl_Help_Link *temp; // New link char *target; // Pointer to target name if (nlinks_ >= alinks_) { alinks_ += 16; if (alinks_ == 16) links_ = (Fl_Help_Link *)malloc(sizeof(Fl_Help_Link) * alinks_); else links_ = (Fl_Help_Link *)realloc(links_, sizeof(Fl_Help_Link) * alinks_); } temp = links_ + nlinks_; temp->x = xx; temp->y = yy; temp->w = xx + ww; temp->h = yy + hh; strlcpy(temp->filename, n, sizeof(temp->filename)); if ((target = strrchr(temp->filename, '#')) != NULL) { *target++ = '\0'; strlcpy(temp->name, target, sizeof(temp->name)); } else temp->name[0] = '\0'; nlinks_ ++; } // // 'Fl_Help_View::add_target()' - Add a new target to the list. // void Fl_Help_View::add_target(const char *n, // I - Name of target int yy) // I - Y position of target { Fl_Help_Target *temp; // New target if (ntargets_ >= atargets_) { atargets_ += 16; if (atargets_ == 16) targets_ = (Fl_Help_Target *)malloc(sizeof(Fl_Help_Target) * atargets_); else targets_ = (Fl_Help_Target *)realloc(targets_, sizeof(Fl_Help_Target) * atargets_); } temp = targets_ + ntargets_; temp->y = yy; strlcpy(temp->name, n, sizeof(temp->name)); ntargets_ ++; } // // 'Fl_Help_View::compare_targets()' - Compare two targets. // int // O - Result of comparison Fl_Help_View::compare_targets(const Fl_Help_Target *t0, // I - First target const Fl_Help_Target *t1) // I - Second target { return (strcasecmp(t0->name, t1->name)); } // // 'Fl_Help_View::do_align()' - Compute the alignment for a line in a block. // int // O - New line Fl_Help_View::do_align(Fl_Help_Block *block, // I - Block to add to int line, // I - Current line int xx, // I - Current X position int a, // I - Current alignment int &l) // IO - Starting link { int offset; // Alignment offset switch (a) { case RIGHT : // Right align offset = block->w - xx; break; case CENTER : // Center offset = (block->w - xx) / 2; break; default : // Left align offset = 0; break; } block->line[line] = block->x + offset; if (line < 31) line ++; while (l < nlinks_) { links_[l].x += offset; links_[l].w += offset; l ++; } return (line); } // // 'Fl_Help_View::draw()' - Draw the Fl_Help_View widget. // void Fl_Help_View::draw() { int i; // Looping var const Fl_Help_Block *block; // Pointer to current block const char *ptr, // Pointer to text in block *attrs; // Pointer to start of element attributes char *s, // Pointer into buffer buf[1024], // Text buffer attr[1024]; // Attribute buffer int xx, yy, ww, hh; // Current positions and sizes int line; // Current line unsigned char font, fsize; // Current font and size int head, pre, // Flags for text needspace; // Do we need whitespace? Fl_Boxtype b = box() ? box() : FL_DOWN_BOX; // Box to draw... int underline, // Underline text? xtra_ww; // Extra width for underlined space between words // Draw the scrollbar(s) and box first... ww = w() ; hh = h(); i = 0; draw_box(b, x(), y(), ww, hh, bgcolor_); if (hscrollbar_.visible()) { draw_child(hscrollbar_); hh -= 17; i ++; } if (scrollbar_.visible()) { draw_child(scrollbar_); ww -= 17; i ++; } if (i == 2) { fl_color(FL_GRAY); fl_rectf(x() + ww - Fl::box_dw(b) + Fl::box_dx(b), y() + hh - Fl::box_dh(b) + Fl::box_dy(b), 17, 17); } if (!value_) return; // Clip the drawing to the inside of the box... fl_push_clip(x() + Fl::box_dx(b), y() + Fl::box_dy(b), ww - Fl::box_dw(b), hh - Fl::box_dh(b)); fl_color(textcolor_); // Draw all visible blocks... for (i = 0, block = blocks_; i < nblocks_; i ++, block ++) if ((block->y + block->h - y()) >= topline_ && (block->y - y()) < (topline_ + h())) { line = 0; xx = block->line[line]; yy = block->y - topline_; hh = 0; pre = 0; head = 0; needspace = 0; underline = 0; initfont(font, fsize); for (ptr = block->start, s = buf; ptr < block->end;) { if ((*ptr == '<' || isspace(*ptr)) && s > buf) { if (!head && !pre) { // Check width... *s = '\0'; s = buf; ww = (int)fl_width(buf); if (needspace && xx > block->x) xx += (int)fl_width(' '); if ((xx + ww) > block->w) { if (line < 31) line ++; xx = block->line[line]; yy += hh; hh = 0; } fl_draw(buf, xx + x() - leftline_, yy + y()); if (underline) { xtra_ww = isspace(*ptr)?(int)fl_width(' '):0; fl_xyline(xx + x() - leftline_, yy + y() + 1, xx + x() - leftline_ + ww + xtra_ww); } xx += ww; if ((fsize + 2) > hh) hh = fsize + 2; needspace = 0; } else if (pre) { while (isspace(*ptr)) { if (*ptr == '\n') { *s = '\0'; s = buf; fl_draw(buf, xx + x() - leftline_, yy + y()); if (underline) fl_xyline(xx + x() - leftline_, yy + y() + 1, xx + x() - leftline_ + (int)fl_width(buf)); if (line < 31) line ++; xx = block->line[line]; yy += hh; hh = fsize + 2; } else if (*ptr == '\t') { // Do tabs every 8 columns... while (((s - buf) & 7)) *s++ = ' '; } else *s++ = ' '; if ((fsize + 2) > hh) hh = fsize + 2; ptr ++; } if (s > buf) { *s = '\0'; s = buf; fl_draw(buf, xx + x() - leftline_, yy + y()); ww = (int)fl_width(buf); if (underline) fl_xyline(xx + x() - leftline_, yy + y() + 1, xx + x() - leftline_ + ww); xx += ww; } needspace = 0; } else { s = buf; while (isspace(*ptr)) ptr ++; } } if (*ptr == '<') { ptr ++; if (strncmp(ptr, "!--", 3) == 0) { // Comment... ptr += 3; if ((ptr = strstr(ptr, "-->")) != NULL) { ptr += 3; continue; } else break; } while (*ptr && *ptr != '>' && !isspace(*ptr)) if (s < (buf + sizeof(buf) - 1)) *s++ = *ptr++; else ptr ++; *s = '\0'; s = buf; attrs = ptr; while (*ptr && *ptr != '>') ptr ++; if (*ptr == '>') ptr ++; if (strcasecmp(buf, "HEAD") == 0) head = 1; else if (strcasecmp(buf, "BR") == 0) { if (line < 31) line ++; xx = block->line[line]; yy += hh; hh = 0; } else if (strcasecmp(buf, "HR") == 0) { fl_line(block->x + x(), yy + y() - fl_height(), block->w + x(), yy + y() - fl_height()); if (line < 31) line ++; xx = block->line[line]; yy += 2 * hh; hh = 0; } else if (strcasecmp(buf, "CENTER") == 0 || strcasecmp(buf, "P") == 0 || strcasecmp(buf, "H1") == 0 || strcasecmp(buf, "H2") == 0 || strcasecmp(buf, "H3") == 0 || strcasecmp(buf, "H4") == 0 || strcasecmp(buf, "H5") == 0 || strcasecmp(buf, "H6") == 0 || strcasecmp(buf, "UL") == 0 || strcasecmp(buf, "OL") == 0 || strcasecmp(buf, "DL") == 0 || strcasecmp(buf, "LI") == 0 || strcasecmp(buf, "DD") == 0 || strcasecmp(buf, "DT") == 0 || strcasecmp(buf, "PRE") == 0) { if (tolower(buf[0]) == 'h') { font = FL_HELVETICA_BOLD; fsize = (uchar)(textsize_ + '7' - buf[1]); } else if (strcasecmp(buf, "DT") == 0) { font = (uchar)(textfont_ | FL_BOLD); fsize = textsize_; } else if (strcasecmp(buf, "PRE") == 0) { font = FL_COURIER; fsize = textsize_; pre = 1; } if (strcasecmp(buf, "LI") == 0) { #ifdef __APPLE_QUARTZ__ fl_font(FL_SYMBOL, fsize); fl_draw("\245", xx - fsize + x() - leftline_, yy + y()); #else // fl_font(FL_SYMBOL, fsize); fl_draw("-", xx - fsize + x() - leftline_, yy + y()); #endif } pushfont(font, fsize); } else if (strcasecmp(buf, "A") == 0 && get_attr(attrs, "HREF", attr, sizeof(attr)) != NULL) { fl_color(linkcolor_); underline = 1; } else if (strcasecmp(buf, "/A") == 0) { fl_color(textcolor_); underline = 0; } else if (strcasecmp(buf, "FONT") == 0) { if (get_attr(attrs, "COLOR", attr, sizeof(attr)) != NULL) { fl_color(get_color(attr, textcolor_)); } if (get_attr(attrs, "FACE", attr, sizeof(attr)) != NULL) { if (!strncasecmp(attr, "helvetica", 9) || !strncasecmp(attr, "arial", 5) || !strncasecmp(attr, "sans", 4)) font = FL_HELVETICA; else if (!strncasecmp(attr, "times", 5) || !strncasecmp(attr, "serif", 5)) font = FL_TIMES; else if (!strncasecmp(attr, "symbol", 6)) font = FL_SYMBOL; else font = FL_COURIER; } if (get_attr(attrs, "SIZE", attr, sizeof(attr)) != NULL) { if (isdigit(attr[0] & 255)) { // Absolute size fsize = (int)(textsize_ * pow(1.2, atof(attr) - 3.0)); } else { // Relative size fsize = (int)(fsize * pow(1.2, atof(attr) - 3.0)); } } pushfont(font, fsize); } if (strcasecmp(buf, "SMALL") == 0) { pushfont(font, fsize=4*fsize/5); } else if (strcasecmp(buf, "/FONT") == 0) { fl_color(textcolor_); popfont(font, fsize); } else if (strcasecmp(buf, "U") == 0) underline = 1; else if (strcasecmp(buf, "/U") == 0) underline = 0; else if (strcasecmp(buf, "B") == 0 || strcasecmp(buf, "STRONG") == 0) pushfont(font |= FL_BOLD, fsize); else if (strcasecmp(buf, "TD") == 0 || strcasecmp(buf, "TH") == 0) { int tx, ty, tw, th; if (tolower(buf[1]) == 'h') pushfont(font |= FL_BOLD, fsize); else pushfont(font = textfont_, fsize); tx = block->x - 4 - leftline_; ty = block->y - topline_ - fsize - 3; tw = block->w - block->x + 7; th = block->h + fsize - 5; if (tx < 0) { tw += tx; tx = 0; } if (ty < 0) { th += ty; ty = 0; } tx += x(); ty += y(); if (block->bgcolor != bgcolor_) { fl_color(block->bgcolor); fl_rectf(tx, ty, tw, th); fl_color(textcolor_); } if (block->border) fl_rect(tx, ty, tw, th); } else if (strcasecmp(buf, "EM") == 0) pushfont(font |= FL_ITALIC, fsize); else if (strcasecmp(buf, "I") == 0) { font &= (FL_BOLD | FL_ITALIC); font |= FL_HELVETICA; pushfont(font, fsize); } else if (strcasecmp(buf, "CODE") == 0 || strcasecmp(buf, "TT") == 0) pushfont(font = FL_COURIER, fsize); else if (strcasecmp(buf, "KBD") == 0) pushfont(font = FL_COURIER_BOLD, fsize); else if (strcasecmp(buf, "VAR") == 0) pushfont(font = FL_COURIER_ITALIC, fsize); else if (strcasecmp(buf, "/HEAD") == 0) head = 0; else if (strcasecmp(buf, "/H1") == 0 || strcasecmp(buf, "/H2") == 0 || strcasecmp(buf, "/H3") == 0 || strcasecmp(buf, "/H4") == 0 || strcasecmp(buf, "/H5") == 0 || strcasecmp(buf, "/H6") == 0 || strcasecmp(buf, "/B") == 0 || strcasecmp(buf, "/STRONG") == 0 || strcasecmp(buf, "/I") == 0 || strcasecmp(buf, "/EM") == 0 || strcasecmp(buf, "/CODE") == 0 || strcasecmp(buf, "/TT") == 0 || strcasecmp(buf, "/KBD") == 0 || strcasecmp(buf, "/SMALL") == 0 || strcasecmp(buf, "/VAR") == 0) popfont(font, fsize); else if (strcasecmp(buf, "/PRE") == 0) { popfont(font, fsize); pre = 0; } else if (strcasecmp(buf, "IMG") == 0) { Fl_Image *img = 0; int width, height; char wattr[8], hattr[8]; get_attr(attrs, "WIDTH", wattr, sizeof(wattr)); get_attr(attrs, "HEIGHT", hattr, sizeof(hattr)); width = get_length(wattr); height = get_length(hattr); if (get_attr(attrs, "SRC", attr, sizeof(attr))) { img = get_image(attr, width, height); if (!width) width = img->w(); if (!height) height = img->h(); } if (!width || !height) { if (get_attr(attrs, "ALT", attr, sizeof(attr)) == NULL) { strcpy(attr, "IMG"); } } ww = width; if (needspace && xx > block->x) xx += (int)fl_width(' '); if ((xx + ww) > block->w) { if (line < 31) line ++; xx = block->line[line]; yy += hh; hh = 0; } if (img) img->draw(xx + x() - leftline_, yy + y() - fl_height() + fl_descent() + 2); xx += ww; if ((height + 2) > hh) hh = height + 2; needspace = 0; } } else if (*ptr == '\n' && pre) { *s = '\0'; s = buf; fl_draw(buf, xx + x() - leftline_, yy + y()); if (line < 31) line ++; xx = block->line[line]; yy += hh; hh = fsize + 2; needspace = 0; ptr ++; } else if (isspace(*ptr)) { if (pre) { if (*ptr == ' ') *s++ = ' '; else { // Do tabs every 8 columns... while (((s - buf) & 7)) *s++ = ' '; } } ptr ++; needspace = 1; } else if (*ptr == '&') { ptr ++; int qch = quote_char(ptr); if (qch < 0) *s++ = '&'; else { *s++ = qch; ptr = strchr(ptr, ';') + 1; } if ((fsize + 2) > hh) hh = fsize + 2; } else { *s++ = *ptr++; if ((fsize + 2) > hh) hh = fsize + 2; } } *s = '\0'; if (s > buf && !pre && !head) { ww = (int)fl_width(buf); if (needspace && xx > block->x) xx += (int)fl_width(' '); if ((xx + ww) > block->w) { if (line < 31) line ++; xx = block->line[line]; yy += hh; hh = 0; } } if (s > buf && !head) { fl_draw(buf, xx + x() - leftline_, yy + y()); if (underline) fl_xyline(xx + x() - leftline_, yy + y() + 1, xx + x() - leftline_ + ww); } } fl_pop_clip(); } // // 'Fl_Help_View::find()' - Find the specified string... // int // O - Matching position or -1 if not found Fl_Help_View::find(const char *s, // I - String to find int p) // I - Starting position { int i, // Looping var c; // Current character Fl_Help_Block *b; // Current block const char *bp, // Block matching pointer *bs, // Start of current comparison *sp; // Search string pointer // Range check input and value... if (!s || !value_) return -1; if (p < 0 || p >= (int)strlen(value_)) p = 0; else if (p > 0) p ++; // Look for the string... for (i = nblocks_, b = blocks_; i > 0; i --, b ++) { if (b->end < (value_ + p)) continue; if (b->start < (value_ + p)) bp = value_ + p; else bp = b->start; for (sp = s, bs = bp; *sp && *bp && bp < b->end; bp ++) { if (*bp == '<') { // skip to end of element... while (*bp && bp < b->end && *bp != '>') bp ++; continue; } else if (*bp == '&') { // decode HTML entity... if ((c = quote_char(bp + 1)) < 0) c = '&'; else bp = strchr(bp + 1, ';') + 1; } else c = *bp; if (tolower(*sp) == tolower(c)) sp ++; else { // No match, so reset to start of search... sp = s; bs ++; bp = bs; } } if (!*sp) { // Found a match! topline(b->y - b->h); return (b->end - value_); } } // No match! return (-1); } // // 'Fl_Help_View::format()' - Format the help text. // void Fl_Help_View::format() { int i; // Looping var int done; // Are we done yet? Fl_Help_Block *block, // Current block *cell; // Current table cell int cells[MAX_COLUMNS], // Cells in the current row... row; // Current table row (block number) const char *ptr, // Pointer into block *start, // Pointer to start of element *attrs; // Pointer to start of element attributes char *s, // Pointer into buffer buf[1024], // Text buffer attr[1024], // Attribute buffer wattr[1024], // Width attribute buffer hattr[1024], // Height attribute buffer linkdest[1024]; // Link destination int xx, yy, ww, hh; // Size of current text fragment int line; // Current line in block int links; // Links for current line unsigned char font, fsize; // Current font and size unsigned char border; // Draw border? int talign, // Current alignment newalign, // New alignment head, // In the section? pre, //
 text?
		needspace;	// Do we need whitespace?
  int		table_width,	// Width of table
		table_offset;	// Offset of table
  int		column,		// Current table column number
		columns[MAX_COLUMNS];
				// Column widths
  Fl_Color	tc, rc;		// Table/row background color
  Fl_Boxtype	b = box() ? box() : FL_DOWN_BOX;
				// Box to draw...


  // Reset document width...
  hsize_ = w() - 24;

  done = 0;
  while (!done)
  {
    // Reset state variables...
    done       = 1;
    nblocks_   = 0;
    nlinks_    = 0;
    ntargets_  = 0;
    size_      = 0;
    bgcolor_   = color();
    textcolor_ = textcolor();
    linkcolor_ = selection_color();

    tc = rc = bgcolor_;

    strcpy(title_, "Untitled");

    if (!value_)
      return;

    // Setup for formatting...
    initfont(font, fsize);

    line         = 0;
    links        = 0;
    xx           = 4;
    yy           = fsize + 2;
    ww           = 0;
    column       = 0;
    border       = 0;
    hh           = 0;
    block        = add_block(value_, xx, yy, hsize_, 0);
    row          = 0;
    head         = 0;
    pre          = 0;
    talign       = LEFT;
    newalign     = LEFT;
    needspace    = 0;
    linkdest[0]  = '\0';
    table_offset = 0;

    for (ptr = value_, s = buf; *ptr;)
    {
      if ((*ptr == '<' || isspace(*ptr)) && s > buf)
      {
        // Get width...
        *s = '\0';
        ww = (int)fl_width(buf);

	if (!head && !pre)
	{
          // Check width...
          if (ww > hsize_) {
	    hsize_ = ww;
	    done   = 0;
	    break;
	  }

          if (needspace && xx > block->x)
	    ww += (int)fl_width(' ');

  //        printf("line = %d, xx = %d, ww = %d, block->x = %d, block->w = %d\n",
  //	       line, xx, ww, block->x, block->w);

          if ((xx + ww) > block->w)
	  {
            line     = do_align(block, line, xx, newalign, links);
	    xx       = block->x;
	    yy       += hh;
	    block->h += hh;
	    hh       = 0;
	  }

          if (linkdest[0])
	    add_link(linkdest, xx, yy - fsize, ww, fsize);

	  xx += ww;
	  if ((fsize + 2) > hh)
	    hh = fsize + 2;

	  needspace = 0;
	}
	else if (pre)
	{
          // Add a link as needed...
          if (linkdest[0])
	    add_link(linkdest, xx, yy - hh, ww, hh);

	  xx += ww;
	  if ((fsize + 2) > hh)
	    hh = fsize + 2;

          // Handle preformatted text...
	  while (isspace(*ptr))
	  {
	    if (*ptr == '\n')
	    {
              if (xx > hsize_) break;

              line     = do_align(block, line, xx, newalign, links);
              xx       = block->x;
	      yy       += hh;
	      block->h += hh;
	      hh       = fsize + 2;
	    }
	    else
              xx += (int)fl_width(' ');

            if ((fsize + 2) > hh)
	      hh = fsize + 2;

            ptr ++;
	  }

          if (xx > hsize_) {
	    hsize_ = xx;
	    done   = 0;
	    break;
	  }

	  needspace = 0;
	}
	else
	{
          // Handle normal text or stuff in the  section...
	  while (isspace(*ptr))
            ptr ++;
	}

	s = buf;
      }

      if (*ptr == '<')
      {
	start = ptr;
	ptr ++;

        if (strncmp(ptr, "!--", 3) == 0)
	{
	  // Comment...
	  ptr += 3;
	  if ((ptr = strstr(ptr, "-->")) != NULL)
	  {
	    ptr += 3;
	    continue;
	  }
	  else
	    break;
	}

	while (*ptr && *ptr != '>' && !isspace(*ptr))
          if (s < (buf + sizeof(buf) - 1))
            *s++ = *ptr++;
	  else
	    ptr ++;

	*s = '\0';
	s = buf;

//        puts(buf);

	attrs = ptr;
	while (*ptr && *ptr != '>')
          ptr ++;

	if (*ptr == '>')
          ptr ++;

	if (strcasecmp(buf, "HEAD") == 0)
          head = 1;
	else if (strcasecmp(buf, "/HEAD") == 0)
          head = 0;
	else if (strcasecmp(buf, "TITLE") == 0)
	{
          // Copy the title in the document...
          for (s = title_;
	       *ptr != '<' && *ptr && s < (title_ + sizeof(title_) - 1);
	       *s++ = *ptr++);

	  *s = '\0';
	  s = buf;
	}
	else if (strcasecmp(buf, "A") == 0)
	{
          if (get_attr(attrs, "NAME", attr, sizeof(attr)) != NULL)
	    add_target(attr, yy - fsize - 2);

	  if (get_attr(attrs, "HREF", attr, sizeof(attr)) != NULL)
	    strlcpy(linkdest, attr, sizeof(linkdest));
	}
	else if (strcasecmp(buf, "/A") == 0)
          linkdest[0] = '\0';
	else if (strcasecmp(buf, "BODY") == 0)
	{
          bgcolor_   = get_color(get_attr(attrs, "BGCOLOR", attr, sizeof(attr)),
	                	 color());
          textcolor_ = get_color(get_attr(attrs, "TEXT", attr, sizeof(attr)),
	                	 textcolor());
          linkcolor_ = get_color(get_attr(attrs, "LINK", attr, sizeof(attr)),
	                	 selection_color());
	}
	else if (strcasecmp(buf, "BR") == 0)
	{
          line     = do_align(block, line, xx, newalign, links);
          xx       = block->x;
	  block->h += hh;
          yy       += hh;
	  hh       = 0;
	}
	else if (strcasecmp(buf, "CENTER") == 0 ||
        	 strcasecmp(buf, "P") == 0 ||
        	 strcasecmp(buf, "H1") == 0 ||
		 strcasecmp(buf, "H2") == 0 ||
		 strcasecmp(buf, "H3") == 0 ||
		 strcasecmp(buf, "H4") == 0 ||
		 strcasecmp(buf, "H5") == 0 ||
		 strcasecmp(buf, "H6") == 0 ||
		 strcasecmp(buf, "UL") == 0 ||
		 strcasecmp(buf, "OL") == 0 ||
		 strcasecmp(buf, "DL") == 0 ||
		 strcasecmp(buf, "LI") == 0 ||
		 strcasecmp(buf, "DD") == 0 ||
		 strcasecmp(buf, "DT") == 0 ||
		 strcasecmp(buf, "HR") == 0 ||
		 strcasecmp(buf, "PRE") == 0 ||
		 strcasecmp(buf, "TABLE") == 0)
	{
          block->end = start;
          line       = do_align(block, line, xx, newalign, links);
          xx         = block->x;
          block->h   += hh;

          if (strcasecmp(buf, "UL") == 0 ||
	      strcasecmp(buf, "OL") == 0 ||
	      strcasecmp(buf, "DL") == 0)
          {
	    block->h += fsize + 2;
	    xx       += 2 * fsize;
	  }
          else if (strcasecmp(buf, "DD") == 0)
          {
	    xx       += 2 * fsize;
	  }
          else if (strcasecmp(buf, "TABLE") == 0)
	  {
	    if (get_attr(attrs, "BORDER", attr, sizeof(attr)))
	      border = (uchar)atoi(attr);
	    else
	      border = 0;

            tc = rc = get_color(get_attr(attrs, "BGCOLOR", attr, sizeof(attr)), bgcolor_);

	    block->h += fsize + 2;

            format_table(&table_width, columns, start);

            if ((xx + table_width) > hsize_) {
#ifdef DEBUG
              printf("xx=%d, table_width=%d, hsize_=%d\n", xx, table_width,
	             hsize_);
#endif // DEBUG
	      hsize_ = xx + table_width;
	      done   = 0;
	      break;
	    }

            switch (get_align(attrs, talign))
	    {
	      default :
	          table_offset = 0;
	          break;

	      case CENTER :
	          table_offset = (hsize_ - table_width) / 2 - textsize_;
	          break;

	      case RIGHT :
	          table_offset = hsize_ - table_width - textsize_;
	          break;
	    }

	    column = 0;
	  }

          if (tolower(buf[0]) == 'h' && isdigit(buf[1]))
	  {
	    font  = FL_HELVETICA_BOLD;
	    fsize = (uchar)(textsize_ + '7' - buf[1]);
	  }
	  else if (strcasecmp(buf, "DT") == 0)
	  {
	    font  = (uchar)(textfont_ | FL_BOLD);
	    fsize = textsize_;
	  }
	  else if (strcasecmp(buf, "PRE") == 0)
	  {
	    font  = FL_COURIER;
	    fsize = textsize_;
	    pre   = 1;
	  }
	  else
	  {
	    font  = textfont_;
	    fsize = textsize_;
	  }

	  pushfont(font, fsize);

          yy = block->y + block->h;
          hh = 0;

          if ((tolower(buf[0]) == 'h' && isdigit(buf[1])) ||
	      strcasecmp(buf, "DT") == 0 ||
	      strcasecmp(buf, "P") == 0)
            yy += fsize + 2;
	  else if (strcasecmp(buf, "HR") == 0)
	  {
	    hh += 2 * fsize;
	    yy += fsize;
	  }
	  else if (strcasecmp(buf, "DD") == 0)
	  {
	    yy += fsize/2;
	  }

          if (row)
	    block = add_block(start, xx, yy, block->w, 0);
	  else
	    block = add_block(start, xx, yy, hsize_, 0);

	  needspace = 0;
	  line      = 0;

	  if (strcasecmp(buf, "CENTER") == 0)
	    newalign = talign = CENTER;
	  else
	    newalign = get_align(attrs, talign);
	}
	else if (strcasecmp(buf, "/CENTER") == 0 ||
		 strcasecmp(buf, "/P") == 0 ||
		 strcasecmp(buf, "/H1") == 0 ||
		 strcasecmp(buf, "/H2") == 0 ||
		 strcasecmp(buf, "/H3") == 0 ||
		 strcasecmp(buf, "/H4") == 0 ||
		 strcasecmp(buf, "/H5") == 0 ||
		 strcasecmp(buf, "/H6") == 0 ||
		 strcasecmp(buf, "/PRE") == 0 ||
		 strcasecmp(buf, "/UL") == 0 ||
		 strcasecmp(buf, "/OL") == 0 ||
		 strcasecmp(buf, "/DL") == 0 ||
		 strcasecmp(buf, "/DD") == 0 ||
		 strcasecmp(buf, "/TABLE") == 0)
	{
          line       = do_align(block, line, xx, newalign, links);
          xx         = block->x;
          block->end = ptr;

          if (strcasecmp(buf, "/UL") == 0 ||
	      strcasecmp(buf, "/OL") == 0 ||
	      strcasecmp(buf, "/DD") == 0 ||
	      strcasecmp(buf, "/DL") == 0)
	  {
	    xx       -= 2 * fsize;
//	    block->h += fsize + 2;
	  }
          else if (strcasecmp(buf, "/TABLE") == 0)
          {
	    block->h += fsize + 2;
            // the current block is *not* the table block, so the current xx is
            // meaningless. Set it back to page x, so the next block will be aligned
            // reasonably. This fails fro table-in-table html!
            xx = 4;
          }
	  else if (strcasecmp(buf, "/PRE") == 0)
	  {
	    pre = 0;
	    hh  = 0;
	  }
	  else if (strcasecmp(buf, "/CENTER") == 0)
	    talign = LEFT;

          popfont(font, fsize);

          while (isspace(*ptr))
	    ptr ++;

          block->h += hh;
          yy       += hh;

          if (tolower(buf[2]) == 'l')
            yy += fsize + 2;

          if (row)
	    block = add_block(ptr, xx, yy, block->w, 0);
	  else
	    block = add_block(ptr, xx, yy, hsize_, 0);

	  needspace = 0;
	  hh        = 0;
	  line      = 0;
	  newalign  = talign;
	}
	else if (strcasecmp(buf, "TR") == 0)
	{
          block->end = start;
          line       = do_align(block, line, xx, newalign, links);
          xx         = block->x;
          block->h   += hh;

          if (row)
	  {
            yy = blocks_[row].y + blocks_[row].h;

	    for (cell = blocks_ + row + 1; cell <= block; cell ++)
	      if ((cell->y + cell->h) > yy)
		yy = cell->y + cell->h;

            block = blocks_ + row;

            block->h = yy - block->y + 2;

	    for (i = 0; i < column; i ++)
	      if (cells[i])
	      {
		cell = blocks_ + cells[i];
		cell->h = block->h;
	      }
	  }

          memset(cells, 0, sizeof(cells));

	  yy        = block->y + block->h - 4;
	  hh        = 0;
          block     = add_block(start, xx, yy, hsize_, 0);
	  row       = block - blocks_;
	  needspace = 0;
	  column    = 0;
	  line      = 0;

          rc = get_color(get_attr(attrs, "BGCOLOR", attr, sizeof(attr)), tc);
	}
	else if (strcasecmp(buf, "/TR") == 0 && row)
	{
          line       = do_align(block, line, xx, newalign, links);
          block->end = start;
	  block->h   += hh;

          xx = blocks_[row].x;

          yy = blocks_[row].y + blocks_[row].h;

	  for (cell = blocks_ + row + 1; cell <= block; cell ++)
	    if ((cell->y + cell->h) > yy)
	      yy = cell->y + cell->h;

          block = blocks_ + row;

          block->h = yy - block->y + 2;

	  for (i = 0; i < column; i ++)
	    if (cells[i])
	    {
	      cell = blocks_ + cells[i];
	      cell->h = block->h;
	    }

	  yy        = block->y + block->h - 4;
          block     = add_block(start, xx, yy, hsize_, 0);
	  needspace = 0;
	  row       = 0;
	  line      = 0;
	}
	else if ((strcasecmp(buf, "TD") == 0 ||
                  strcasecmp(buf, "TH") == 0) && row)
	{
          int	colspan;		// COLSPAN attribute


          line       = do_align(block, line, xx, newalign, links);
          block->end = start;
	  block->h   += hh;

          if (strcasecmp(buf, "TH") == 0)
	    font = (uchar)(textfont_ | FL_BOLD);
	  else
	    font = textfont_;

          fsize = textsize_;

          xx = blocks_[row].x + fsize + 3 + table_offset;
	  for (i = 0; i < column; i ++)
	    xx += columns[i] + 6;

          if (get_attr(attrs, "COLSPAN", attr, sizeof(attr)) != NULL)
	    colspan = atoi(attr);
	  else
	    colspan = 1;

          for (i = 0, ww = -6; i < colspan; i ++)
	    ww += columns[column + i] + 6;

          if (block->end == block->start && nblocks_ > 1)
	  {
	    nblocks_ --;
	    block --;
	  }

	  pushfont(font, fsize);

	  yy        = blocks_[row].y;
	  hh        = 0;
          block     = add_block(start, xx, yy, xx + ww, 0, border);
	  needspace = 0;
	  line      = 0;
	  newalign  = get_align(attrs, tolower(buf[1]) == 'h' ? CENTER : LEFT);
	  talign    = newalign;

          cells[column] = block - blocks_;

	  column += colspan;

          block->bgcolor = get_color(get_attr(attrs, "BGCOLOR", attr,
	                                      sizeof(attr)), rc);
	}
	else if ((strcasecmp(buf, "/TD") == 0 ||
                  strcasecmp(buf, "/TH") == 0) && row)
	{
          popfont(font, fsize);
	}
	  else if (strcasecmp(buf, "FONT") == 0)
	  {
            if (get_attr(attrs, "FACE", attr, sizeof(attr)) != NULL) {
	      if (!strncasecmp(attr, "helvetica", 9) ||
	          !strncasecmp(attr, "arial", 5) ||
		  !strncasecmp(attr, "sans", 4)) font = FL_HELVETICA;
              else if (!strncasecmp(attr, "times", 5) ||
	               !strncasecmp(attr, "serif", 5)) font = FL_TIMES;
              else if (!strncasecmp(attr, "symbol", 6)) font = FL_SYMBOL;
	      else font = FL_COURIER;
            }

            if (get_attr(attrs, "SIZE", attr, sizeof(attr)) != NULL) {
              if (isdigit(attr[0] & 255)) {
	        // Absolute size
	        fsize = (int)(textsize_ * pow(1.2, atoi(attr) - 3.0));
	      } else {
	        // Relative size
	        fsize = (int)(fsize * pow(1.2, atoi(attr)));
	      }
	    }

            pushfont(font, fsize);
	  }
	  else if (strcasecmp(buf, "/FONT") == 0)
	    popfont(font, fsize);
	else if (strcasecmp(buf, "B") == 0 ||
        	 strcasecmp(buf, "STRONG") == 0)
	  pushfont(font |= FL_BOLD, fsize);
	else if (strcasecmp(buf, "EM") == 0)
	  pushfont(font |= FL_ITALIC, fsize);
	else if (strcasecmp(buf, "I") == 0)
	  pushfont(font = font & (FL_BOLD | FL_ITALIC) | FL_HELVETICA, fsize);
	else if (strcasecmp(buf, "CODE") == 0 ||
	         strcasecmp(buf, "TT") == 0)
	  pushfont(font = FL_COURIER, fsize);
	else if (strcasecmp(buf, "KBD") == 0)
	  pushfont(font = FL_COURIER_BOLD, fsize);
	else if (strcasecmp(buf, "VAR") == 0)
	  pushfont(font = FL_COURIER_ITALIC, fsize);
	else if (strcasecmp(buf, "SMALL") == 0)
	  pushfont(font, fsize=4*fsize/5);
	else if (strcasecmp(buf, "/B") == 0 ||
		 strcasecmp(buf, "/STRONG") == 0 ||
		 strcasecmp(buf, "/I") == 0 ||
		 strcasecmp(buf, "/EM") == 0 ||
		 strcasecmp(buf, "/CODE") == 0 ||
		 strcasecmp(buf, "/TT") == 0 ||
		 strcasecmp(buf, "/KBD") == 0 ||
		 strcasecmp(buf, "/SMALL") == 0 ||
		 strcasecmp(buf, "/VAR") == 0)
	  popfont(font, fsize);
	else if (strcasecmp(buf, "IMG") == 0)
	{
	  Fl_Image	*img = 0;
	  int		width;
	  int		height;


          get_attr(attrs, "WIDTH", wattr, sizeof(wattr));
          get_attr(attrs, "HEIGHT", hattr, sizeof(hattr));
	  width  = get_length(wattr);
	  height = get_length(hattr);

	  if (get_attr(attrs, "SRC", attr, sizeof(attr))) {
	    img    = get_image(attr, width, height);
	    width  = img->w();
	    height = img->h();
	  }

	  ww = width;

          if (ww > hsize_) {
	    hsize_ = ww;
	    done   = 0;
	    break;
	  }

	  if (needspace && xx > block->x)
	    ww += (int)fl_width(' ');

	  if ((xx + ww) > block->w)
	  {
	    line     = do_align(block, line, xx, newalign, links);
	    xx       = block->x;
	    yy       += hh;
	    block->h += hh;
	    hh       = 0;
	  }

	  if (linkdest[0])
	    add_link(linkdest, xx, yy - height, ww, height);

	  xx += ww;
	  if ((height + 2) > hh)
	    hh = height + 2;

	  needspace = 0;
	}
      }
      else if (*ptr == '\n' && pre)
      {
	if (linkdest[0])
	  add_link(linkdest, xx, yy - hh, ww, hh);

        if (xx > hsize_) {
	  hsize_ = xx;
          done   = 0;
	  break;
	}

	line      = do_align(block, line, xx, newalign, links);
	xx        = block->x;
	yy        += hh;
	block->h  += hh;
	needspace = 0;
	ptr ++;
      }
      else if (isspace(*ptr))
      {
	needspace = 1;

	ptr ++;
      }
      else if (*ptr == '&' && s < (buf + sizeof(buf) - 1))
      {
	ptr ++;

        int qch = quote_char(ptr);

	if (qch < 0)
	  *s++ = '&';
	else {
	  *s++ = qch;
	  ptr = strchr(ptr, ';') + 1;
	}

	if ((fsize + 2) > hh)
          hh = fsize + 2;
      }
      else
      {
	if (s < (buf + sizeof(buf) - 1))
          *s++ = *ptr++;
	else
          ptr ++;

	if ((fsize + 2) > hh)
          hh = fsize + 2;
      }
    }

    if (s > buf && !head)
    {
      *s = '\0';
      ww = (int)fl_width(buf);

  //    printf("line = %d, xx = %d, ww = %d, block->x = %d, block->w = %d\n",
  //	   line, xx, ww, block->x, block->w);

      if (ww > hsize_) {
	hsize_ = ww;
	done   = 0;
	break;
      }

      if (needspace && xx > block->x)
	ww += (int)fl_width(' ');

      if ((xx + ww) > block->w)
      {
	line     = do_align(block, line, xx, newalign, links);
	xx       = block->x;
	yy       += hh;
	block->h += hh;
	hh       = 0;
      }

      if (linkdest[0])
	add_link(linkdest, xx, yy - fsize, ww, fsize);

      xx += ww;
    }

    do_align(block, line, xx, newalign, links);

    block->end = ptr;
    size_      = yy + hh;
  }


  if (ntargets_ > 1)
    qsort(targets_, ntargets_, sizeof(Fl_Help_Target),
          (compare_func_t)compare_targets);

  int dx = Fl::box_dw(b) - Fl::box_dx(b);
  int dy = Fl::box_dh(b) - Fl::box_dy(b);

  if (hsize_ > (w() - 24)) {
    hscrollbar_.show();

    if (size_ < (h() - 24)) {
      scrollbar_.hide();
      hscrollbar_.resize(x() + Fl::box_dx(b), y() + h() - 17 - dy, w() - Fl::box_dw(b), 17);
    } else {
      scrollbar_.show();
      scrollbar_.resize(x() + w() - 17 - dx, y() + Fl::box_dy(b), 17, h() - 17 - Fl::box_dh(b));
      hscrollbar_.resize(x() + Fl::box_dx(b), y() + h() - 17 - dy, w() - 17 - Fl::box_dw(b), 17);
    }
  } else {
    hscrollbar_.hide();

    if (size_ < (h() - 8)) scrollbar_.hide();
    else {
      scrollbar_.resize(x() + w() - 17 - dx, y() + Fl::box_dy(b), 17, h() - Fl::box_dh(b));
      scrollbar_.show();
    }
  }

  // Reset scrolling if it needs to be...
  if (scrollbar_.visible()) {
    int temph = h() - 8;
    if (hscrollbar_.visible()) temph -= 16;
    if ((topline_ + temph) > size_) topline(size_ - temph);
    else topline(topline_);
  } else topline(0);

  if (hscrollbar_.visible()) {
    int tempw = w() - 24;
    if ((leftline_ + tempw) > hsize_) leftline(hsize_ - tempw);
    else leftline(leftline_);
  } else leftline(0);
}


//
// 'Fl_Help_View::format_table()' - Format a table...
//

void
Fl_Help_View::format_table(int        *table_width,	// O - Total table width
                           int        *columns,		// O - Column widths
	                   const char *table)		// I - Pointer to start of table
{
  int		column,					// Current column
		num_columns,				// Number of columns
		colspan,				// COLSPAN attribute
		width,					// Current width
		temp_width,				// Temporary width
		max_width,				// Maximum width
		incell,					// In a table cell?
		pre,					// 
 text?
		needspace;				// Need whitespace?
  char		*s,					// Pointer into buffer
		buf[1024],				// Text buffer
		attr[1024],				// Other attribute
		wattr[1024],				// WIDTH attribute
		hattr[1024];				// HEIGHT attribute
  const char	*ptr,					// Pointer into table
		*attrs,					// Pointer to attributes
		*start;					// Start of element
  int		minwidths[MAX_COLUMNS];			// Minimum widths for each column
  unsigned char	font, fsize;				// Current font and size


  // Clear widths...
  *table_width = 0;
  for (column = 0; column < MAX_COLUMNS; column ++)
  {
    columns[column]   = 0;
    minwidths[column] = 0;
  }

  num_columns = 0;
  colspan     = 0;
  max_width   = 0;
  pre         = 0;
  needspace   = 0;
  font        = fonts_[nfonts_][0];
  fsize       = fonts_[nfonts_][1];

  // Scan the table...
  for (ptr = table, column = -1, width = 0, s = buf, incell = 0; *ptr;)
  {
    if ((*ptr == '<' || isspace(*ptr)) && s > buf && incell)
    {
      // Check width...
      if (needspace)
      {
        *s++      = ' ';
	needspace = 0;
      }

      *s         = '\0';
      temp_width = (int)fl_width(buf);
      s          = buf;

      if (temp_width > minwidths[column])
        minwidths[column] = temp_width;

      width += temp_width;

      if (width > max_width)
        max_width = width;
    }

    if (*ptr == '<')
    {
      start = ptr;

      for (s = buf, ptr ++; *ptr && *ptr != '>' && !isspace(*ptr);)
        if (s < (buf + sizeof(buf) - 1))
          *s++ = *ptr++;
	else
	  ptr ++;

      *s = '\0';
      s = buf;

      attrs = ptr;
      while (*ptr && *ptr != '>')
        ptr ++;

      if (*ptr == '>')
        ptr ++;

      if (strcasecmp(buf, "BR") == 0 ||
	  strcasecmp(buf, "HR") == 0)
      {
        width     = 0;
	needspace = 0;
      }
      else if (strcasecmp(buf, "TABLE") == 0 && start > table)
        break;
      else if (strcasecmp(buf, "CENTER") == 0 ||
               strcasecmp(buf, "P") == 0 ||
               strcasecmp(buf, "H1") == 0 ||
	       strcasecmp(buf, "H2") == 0 ||
	       strcasecmp(buf, "H3") == 0 ||
	       strcasecmp(buf, "H4") == 0 ||
	       strcasecmp(buf, "H5") == 0 ||
	       strcasecmp(buf, "H6") == 0 ||
	       strcasecmp(buf, "UL") == 0 ||
	       strcasecmp(buf, "OL") == 0 ||
	       strcasecmp(buf, "DL") == 0 ||
	       strcasecmp(buf, "LI") == 0 ||
	       strcasecmp(buf, "DD") == 0 ||
	       strcasecmp(buf, "DT") == 0 ||
	       strcasecmp(buf, "PRE") == 0)
      {
        width     = 0;
	needspace = 0;

        if (tolower(buf[0]) == 'h' && isdigit(buf[1]))
	{
	  font  = FL_HELVETICA_BOLD;
	  fsize = (uchar)(textsize_ + '7' - buf[1]);
	}
	else if (strcasecmp(buf, "DT") == 0)
	{
	  font  = (uchar)(textfont_ | FL_BOLD);
	  fsize = textsize_;
	}
	else if (strcasecmp(buf, "PRE") == 0)
	{
	  font  = FL_COURIER;
	  fsize = textsize_;
	  pre   = 1;
	}
	else if (strcasecmp(buf, "LI") == 0)
	{
	  width  += 4 * fsize;
	  font   = textfont_;
	  fsize  = textsize_;
	}
	else
	{
	  font  = textfont_;
	  fsize = textsize_;
	}

	pushfont(font, fsize);
      }
      else if (strcasecmp(buf, "/CENTER") == 0 ||
	       strcasecmp(buf, "/P") == 0 ||
	       strcasecmp(buf, "/H1") == 0 ||
	       strcasecmp(buf, "/H2") == 0 ||
	       strcasecmp(buf, "/H3") == 0 ||
	       strcasecmp(buf, "/H4") == 0 ||
	       strcasecmp(buf, "/H5") == 0 ||
	       strcasecmp(buf, "/H6") == 0 ||
	       strcasecmp(buf, "/PRE") == 0 ||
	       strcasecmp(buf, "/UL") == 0 ||
	       strcasecmp(buf, "/OL") == 0 ||
	       strcasecmp(buf, "/DL") == 0)
      {
        width     = 0;
	needspace = 0;

        popfont(font, fsize);
      }
      else if (strcasecmp(buf, "TR") == 0 || strcasecmp(buf, "/TR") == 0 ||
               strcasecmp(buf, "/TABLE") == 0)
      {
//        printf("%s column = %d, colspan = %d, num_columns = %d\n",
//	       buf, column, colspan, num_columns);

        if (column >= 0)
	{
	  // This is a hack to support COLSPAN...
	  max_width /= colspan;

	  while (colspan > 0)
	  {
	    if (max_width > columns[column])
	      columns[column] = max_width;

	    column ++;
	    colspan --;
	  }
	}

	if (strcasecmp(buf, "/TABLE") == 0)
	  break;

	needspace = 0;
	column    = -1;
	width     = 0;
	max_width = 0;
	incell    = 0;
      }
      else if (strcasecmp(buf, "TD") == 0 ||
               strcasecmp(buf, "TH") == 0)
      {
//        printf("BEFORE column = %d, colspan = %d, num_columns = %d\n",
//	       column, colspan, num_columns);

        if (column >= 0)
	{
	  // This is a hack to support COLSPAN...
	  max_width /= colspan;

	  while (colspan > 0)
	  {
	    if (max_width > columns[column])
	      columns[column] = max_width;

	    column ++;
	    colspan --;
	  }
	}
	else
	  column ++;

        if (get_attr(attrs, "COLSPAN", attr, sizeof(attr)) != NULL)
	  colspan = atoi(attr);
	else
	  colspan = 1;

//        printf("AFTER column = %d, colspan = %d, num_columns = %d\n",
//	       column, colspan, num_columns);

        if ((column + colspan) >= num_columns)
	  num_columns = column + colspan;

	needspace = 0;
	width     = 0;
	incell    = 1;

        if (strcasecmp(buf, "TH") == 0)
	  font = (uchar)(textfont_ | FL_BOLD);
	else
	  font = textfont_;

        fsize = textsize_;

	pushfont(font, fsize);

        if (get_attr(attrs, "WIDTH", attr, sizeof(attr)) != NULL)
	  max_width = get_length(attr);
	else
	  max_width = 0;

//        printf("max_width = %d\n", max_width);
      }
      else if (strcasecmp(buf, "/TD") == 0 ||
               strcasecmp(buf, "/TH") == 0)
      {
	incell = 0;
        popfont(font, fsize);
      }
      else if (strcasecmp(buf, "B") == 0 ||
               strcasecmp(buf, "STRONG") == 0)
	pushfont(font |= FL_BOLD, fsize);
      else if (strcasecmp(buf, "EM") == 0)
        pushfont(font |= FL_ITALIC, fsize);
      else if (strcasecmp(buf, "I") == 0)
        pushfont(font = font & (FL_BOLD | FL_ITALIC) | FL_HELVETICA, fsize);
      else if (strcasecmp(buf, "CODE") == 0 ||
               strcasecmp(buf, "TT") == 0)
	pushfont(font = FL_COURIER, fsize);
      else if (strcasecmp(buf, "KBD") == 0)
	pushfont(font = FL_COURIER_BOLD, fsize);
      else if (strcasecmp(buf, "VAR") == 0)
	pushfont(font = FL_COURIER_ITALIC, fsize);
      else if (strcasecmp(buf, "SMALL") == 0)
	pushfont(font, fsize=4*fsize/5);
      else if (strcasecmp(buf, "/B") == 0 ||
	       strcasecmp(buf, "/STRONG") == 0 ||
	       strcasecmp(buf, "/I") == 0 ||
	       strcasecmp(buf, "/EM") == 0 ||
	       strcasecmp(buf, "/CODE") == 0 ||
	       strcasecmp(buf, "/TT") == 0 ||
	       strcasecmp(buf, "/KBD") == 0 ||
	       strcasecmp(buf, "/SMALL") == 0 ||
	       strcasecmp(buf, "/VAR") == 0)
	popfont(font, fsize);
      else if (strcasecmp(buf, "IMG") == 0 && incell)
      {
	Fl_Image	*img = 0;
	int		iwidth, iheight;


        get_attr(attrs, "WIDTH", wattr, sizeof(wattr));
        get_attr(attrs, "HEIGHT", hattr, sizeof(hattr));
	iwidth  = get_length(wattr);
	iheight = get_length(hattr);

        if (get_attr(attrs, "SRC", attr, sizeof(attr))) {
	  img     = get_image(attr, iwidth, iheight);
	  iwidth  = img->w();
	  iheight = img->h();
	}

	if (iwidth > minwidths[column])
          minwidths[column] = iwidth;

        width += iwidth;
	if (needspace)
	  width += (int)fl_width(' ');

	if (width > max_width)
          max_width = width;

	needspace = 0;
      }
    }
    else if (*ptr == '\n' && pre)
    {
      width     = 0;
      needspace = 0;
      ptr ++;
    }
    else if (isspace(*ptr))
    {
      needspace = 1;

      ptr ++;
    }
    else if (*ptr == '&' && s < (buf + sizeof(buf) - 1))
    {
      ptr ++;

      int qch = quote_char(ptr);

      if (qch < 0)
	*s++ = '&';
      else {
	*s++ = qch;
	ptr = strchr(ptr, ';') + 1;
      }
    }
    else
    {
      if (s < (buf + sizeof(buf) - 1))
        *s++ = *ptr++;
      else
        ptr ++;
    }
  }

  // Now that we have scanned the entire table, adjust the table and
  // cell widths to fit on the screen...
  if (get_attr(table + 6, "WIDTH", attr, sizeof(attr)))
    *table_width = get_length(attr);
  else
    *table_width = 0;

#ifdef DEBUG
  printf("num_columns = %d, table_width = %d\n", num_columns, *table_width);
#endif // DEBUG

  if (num_columns == 0)
    return;

  // Add up the widths...
  for (column = 0, width = 0; column < num_columns; column ++)
    width += columns[column];

#ifdef DEBUG
  printf("width = %d, w() = %d\n", width, w());
  for (column = 0; column < num_columns; column ++)
    printf("    columns[%d] = %d, minwidths[%d] = %d\n", column, columns[column],
           column, minwidths[column]);
#endif // DEBUG

  // Adjust the width if needed...
  int scale_width = *table_width;

  if (scale_width == 0) {
    if (width > (hsize_ - 24)) scale_width = hsize_ - 24;
    else scale_width = width;
  }

  if (width < scale_width) {
#ifdef DEBUG
    printf("Scaling table up to %d from %d...\n", scale_width, width);
#endif // DEBUG

    *table_width = 0;

    scale_width = (scale_width - width) / num_columns;

#ifdef DEBUG
    printf("adjusted scale_width = %d\n", scale_width);
#endif // DEBUG

    for (column = 0; column < num_columns; column ++) {
      columns[column] += scale_width;

      (*table_width) += columns[column];
    }
  }
  else if (width > scale_width) {
#ifdef DEBUG
    printf("Scaling table down to %d from %d...\n", scale_width, width);
#endif // DEBUG

    for (column = 0; column < num_columns; column ++) {
      width       -= minwidths[column];
      scale_width -= minwidths[column];
    }

#ifdef DEBUG
    printf("adjusted width = %d, scale_width = %d\n", width, scale_width);
#endif // DEBUG

    if (width > 0) {
      for (column = 0; column < num_columns; column ++) {
	columns[column] -= minwidths[column];
	columns[column] = scale_width * columns[column] / width;
	columns[column] += minwidths[column];
      }
    }

    *table_width = 0;
    for (column = 0; column < num_columns; column ++) {
      (*table_width) += columns[column];
    }
  }
  else if (*table_width == 0)
    *table_width = width;

#ifdef DEBUG
  printf("FINAL table_width = %d\n", *table_width);
  for (column = 0; column < num_columns; column ++)
    printf("    columns[%d] = %d\n", column, columns[column]);
#endif // DEBUG
}


//
// 'Fl_Help_View::get_align()' - Get an alignment attribute.
//

int					// O - Alignment
Fl_Help_View::get_align(const char *p,	// I - Pointer to start of attrs
                        int        a)	// I - Default alignment
{
  char	buf[255];			// Alignment value


  if (get_attr(p, "ALIGN", buf, sizeof(buf)) == NULL)
    return (a);

  if (strcasecmp(buf, "CENTER") == 0)
    return (CENTER);
  else if (strcasecmp(buf, "RIGHT") == 0)
    return (RIGHT);
  else
    return (LEFT);
}


//
// 'Fl_Help_View::get_attr()' - Get an attribute value from the string.
//

const char *					// O - Pointer to buf or NULL
Fl_Help_View::get_attr(const char *p,		// I - Pointer to start of attributes
                      const char *n,		// I - Name of attribute
		      char       *buf,		// O - Buffer for attribute value
		      int        bufsize)	// I - Size of buffer
{
  char	name[255],				// Name from string
	*ptr,					// Pointer into name or value
	quote;					// Quote


  buf[0] = '\0';

  while (*p && *p != '>')
  {
    while (isspace(*p))
      p ++;

    if (*p == '>' || !*p)
      return (NULL);

    for (ptr = name; *p && !isspace(*p) && *p != '=' && *p != '>';)
      if (ptr < (name + sizeof(name) - 1))
        *ptr++ = *p++;
      else
        p ++;

    *ptr = '\0';

    if (isspace(*p) || !*p || *p == '>')
      buf[0] = '\0';
    else
    {
      if (*p == '=')
        p ++;

      for (ptr = buf; *p && !isspace(*p) && *p != '>';)
        if (*p == '\'' || *p == '\"')
	{
	  quote = *p++;

	  while (*p && *p != quote)
	    if ((ptr - buf + 1) < bufsize)
	      *ptr++ = *p++;
	    else
	      p ++;

          if (*p == quote)
	    p ++;
	}
	else if ((ptr - buf + 1) < bufsize)
	  *ptr++ = *p++;
	else
	  p ++;

      *ptr = '\0';
    }

    if (strcasecmp(n, name) == 0)
      return (buf);
    else
      buf[0] = '\0';

    if (*p == '>')
      return (NULL);
  }

  return (NULL);
}


//
// 'Fl_Help_View::get_color()' - Get an alignment attribute.
//

Fl_Color				// O - Color value
Fl_Help_View::get_color(const char *n,	// I - Color name
                        Fl_Color   c)	// I - Default color value
{
  int	i;				// Looping var
  int	rgb, r, g, b;			// RGB values
  static const struct {			// Color name table
    const char *name;
    int r, g, b;
  }	colors[] = {
    { "black",		0x00, 0x00, 0x00 },
    { "red",		0xff, 0x00, 0x00 },
    { "green",		0x00, 0x80, 0x00 },
    { "yellow",		0xff, 0xff, 0x00 },
    { "blue",		0x00, 0x00, 0xff },
    { "magenta",	0xff, 0x00, 0xff },
    { "fuchsia",	0xff, 0x00, 0xff },
    { "cyan",		0x00, 0xff, 0xff },
    { "aqua",		0x00, 0xff, 0xff },
    { "white",		0xff, 0xff, 0xff },
    { "gray",		0x80, 0x80, 0x80 },
    { "grey",		0x80, 0x80, 0x80 },
    { "lime",		0x00, 0xff, 0x00 },
    { "maroon",		0x80, 0x00, 0x00 },
    { "navy",		0x00, 0x00, 0x80 },
    { "olive",		0x80, 0x80, 0x00 },
    { "purple",		0x80, 0x00, 0x80 },
    { "silver",		0xc0, 0xc0, 0xc0 },
    { "teal",		0x00, 0x80, 0x80 }
  };


  if (!n || !n[0]) return c;

  if (n[0] == '#') {
    // Do hex color lookup
    rgb = strtol(n + 1, NULL, 16);

    if (strlen(n) > 4) {
      r = rgb >> 16;
      g = (rgb >> 8) & 255;
      b = rgb & 255;
    } else {
      r = (rgb >> 8) * 17;
      g = ((rgb >> 4) & 15) * 17;
      b = (rgb & 15) * 17;
    }
    return (fl_rgb_color((uchar)r, (uchar)g, (uchar)b));
  } else {
    for (i = 0; i < (int)(sizeof(colors) / sizeof(colors[0])); i ++)
      if (!strcasecmp(n, colors[i].name)) {
        return fl_rgb_color(colors[i].r, colors[i].g, colors[i].b);
      }
    return c;
  }
}


//
// 'Fl_Help_View::get_image()' - Get an inline image.
//
#ifndef BT_EXTERNAL
Fl_Image *
Fl_Help_View::get_image(const char *name, int W, int H) {

  static std::vector cache;

  int i = 0;

  while (imagelist[i].name && strcmp(imagelist[i].name, name)) i++;

  Fl_Image *ip = 0;

  if (imagelist[i].name) {

    // make sure cache has the required size
    while (cache.size() < i+1)
      cache.push_back(0);

    if (cache[i] == 0)
      cache[i] = new Fl_RGB_Image(imagelist[i].data, imagelist[i].w, imagelist[i].h);

    ip = cache[i];

  } else

    ip = &broken_image;

  return ip;
}
#else
Fl_Image *
Fl_Help_View::get_image(const char *name, int W, int H) {
  const char	*localname;		// Local filename
  char		dir[1024];		// Current directory
  char		temp[1024],		// Temporary filename
		*tempptr;		// Pointer into temporary name
  Fl_Shared_Image *ip;			// Image pointer...

  // See if the image can be found...
  if (strchr(directory_, ':') != NULL && strchr(name, ':') == NULL) {
    if (name[0] == '/') {
      strlcpy(temp, directory_, sizeof(temp));

      if ((tempptr = strrchr(strchr(directory_, ':') + 3, '/')) != NULL) {
        strlcpy(tempptr, name, sizeof(temp) - (tempptr - temp));
      } else {
        strlcat(temp, name, sizeof(temp));
      }
    } else {
      snprintf(temp, sizeof(temp), "%s/%s", directory_, name);
    }

    if (link_) localname = (*link_)(this, temp);
    else localname = temp;
  } else if (name[0] != '/' && strchr(name, ':') == NULL) {
    if (directory_[0]) snprintf(temp, sizeof(temp), "%s/%s", directory_, name);
    else {
      getcwd(dir, sizeof(dir));
      snprintf(temp, sizeof(temp), "file:%s/%s", dir, name);
    }

    if (link_) localname = (*link_)(this, temp);
    else localname = temp;
  } else if (link_) localname = (*link_)(this, name);
  else localname = name;

  if (!localname) return 0;

  if (strncmp(localname, "file:", 5) == 0) localname += 5;

  printf("loading file %s\n", localname);

  if ((ip = Fl_Shared_Image::get(localname, W, H)) == NULL)
    ip = (Fl_Shared_Image *)&broken_image;

  return ip;
}
#endif

//
// 'Fl_Help_View::get_length()' - Get a length value, either absolute or %.
//

int
Fl_Help_View::get_length(const char *l) {	// I - Value
  int	val;					// Integer value

  if (!l[0]) return 0;

  val = atoi(l);
  if (l[strlen(l) - 1] == '%') {
    if (val > 100) val = 100;
    else if (val < 0) val = 0;

    val = val * (hsize_ - 24) / 100;
  }

  return val;
}


//
// 'Fl_Help_View::handle()' - Handle events in the widget.
//

int				// O - 1 if we handled it, 0 otherwise
Fl_Help_View::handle(int event)	// I - Event to handle
{
  int		i;		// Looping var
  int		xx, yy;		// Adjusted mouse position
  Fl_Help_Link	*linkp;		// Current link
  char		target[32];	// Current target


  switch (event)
  {
    case FL_PUSH :
	if (Fl_Group::handle(event))
	  return (1);

    case FL_MOVE :
        xx = Fl::event_x() - x() + leftline_;
	yy = Fl::event_y() - y() + topline_;
	break;

    case FL_LEAVE :
        fl_cursor(FL_CURSOR_DEFAULT);

    default :
	return (Fl_Group::handle(event));
  }

  // Handle mouse clicks on links...
  for (i = nlinks_, linkp = links_; i > 0; i --, linkp ++)
    if (xx >= linkp->x && xx < linkp->w &&
        yy >= linkp->y && yy < linkp->h)
      break;

  if (!i)
  {
    fl_cursor(FL_CURSOR_DEFAULT);
    return (1);
  }

  // Change the cursor for FL_MOTION events, and go to the link for
  // clicks...
  if (event == FL_MOVE)
    fl_cursor(FL_CURSOR_HAND);
  else
  {
    fl_cursor(FL_CURSOR_DEFAULT);

    strlcpy(target, linkp->name, sizeof(target));

    set_changed();

    if (strcmp(linkp->filename, filename_) != 0 && linkp->filename[0])
    {
      char	dir[1024];	// Current directory
      char	temp[1024],	// Temporary filename
		*tempptr;	// Pointer into temporary filename


      if (strchr(directory_, ':') != NULL &&
          strchr(linkp->filename, ':') == NULL)
      {
	if (linkp->filename[0] == '/')
	{
          strlcpy(temp, directory_, sizeof(temp));
          if ((tempptr = strrchr(strchr(directory_, ':') + 3, '/')) != NULL)
	    strlcpy(tempptr, linkp->filename, sizeof(temp));
	  else
	    strlcat(temp, linkp->filename, sizeof(temp));
	}
	else
	  snprintf(temp, sizeof(temp), "%s/%s", directory_, linkp->filename);
      }
      else if (linkp->filename[0] != '/' && strchr(linkp->filename, ':') == NULL)
      {
	if (directory_[0])
	  snprintf(temp, sizeof(temp), "%s/%s", directory_, linkp->filename);
	else
	{
	  getcwd(dir, sizeof(dir));
#ifndef BT_EXTERNAL
	  snprintf(temp, sizeof(temp), "%s", linkp->filename);
#else
	  snprintf(temp, sizeof(temp), "file:%s/%s", dir, linkp->filename);
#endif
	}
      }
      else
        strlcpy(temp, linkp->filename, sizeof(temp));

      if (linkp->name[0])
        snprintf(temp + strlen(temp), sizeof(temp) - strlen(temp), "#%s",
	         linkp->name);

      load(temp);
    }
    else if (target[0])
      topline(target);
    else
      topline(0);

    leftline(0);
  }

  return (1);
}


//
// 'Fl_Help_View::Fl_Help_View()' - Build a Fl_Help_View widget.
//

Fl_Help_View::Fl_Help_View(int        xx,	// I - Left position
                	   int        yy,	// I - Top position
			   int        ww,	// I - Width in pixels
			   int        hh,	// I - Height in pixels
			   const char *l)
    : Fl_Group(xx, yy, ww, hh, l),
      scrollbar_(xx + ww - 17, yy, 17, hh - 17),
      hscrollbar_(xx, yy + hh - 17, ww - 17, 17)
{
  color(FL_BACKGROUND2_COLOR, FL_SELECTION_COLOR);

  title_[0]     = '\0';
  defcolor_     = FL_FOREGROUND_COLOR;
  bgcolor_      = FL_BACKGROUND_COLOR;
  textcolor_    = FL_FOREGROUND_COLOR;
  linkcolor_    = FL_SELECTION_COLOR;
  textfont_     = FL_TIMES;
  textsize_     = 14;
  value_        = NULL;

  ablocks_      = 0;
  nblocks_      = 0;
  blocks_       = (Fl_Help_Block *)0;

  nfonts_       = 0;

  link_         = (Fl_Help_Func *)0;

  alinks_       = 0;
  nlinks_       = 0;
  links_        = (Fl_Help_Link *)0;

  atargets_     = 0;
  ntargets_     = 0;
  targets_      = (Fl_Help_Target *)0;

  directory_[0] = '\0';
  filename_[0]  = '\0';

  topline_      = 0;
  leftline_     = 0;
  size_         = 0;
  hsize_        = 0;

  scrollbar_.value(0, hh, 0, 1);
  scrollbar_.step(8.0);
  scrollbar_.show();
  scrollbar_.callback(scrollbar_callback);

  hscrollbar_.value(0, ww, 0, 1);
  hscrollbar_.step(8.0);
  hscrollbar_.show();
  hscrollbar_.callback(hscrollbar_callback);
  hscrollbar_.type(FL_HORIZONTAL);
  end();

  resize(xx, yy, ww, hh);
}


//
// 'Fl_Help_View::~Fl_Help_View()' - Destroy a Fl_Help_View widget.
//

Fl_Help_View::~Fl_Help_View()
{
  if (nblocks_)
    free(blocks_);
  if (nlinks_)
    free(links_);
  if (ntargets_)
    free(targets_);
  if (value_)
    free((void *)value_);
}


//
// 'Fl_Help_View::load()' - Load the specified file.
//

int				// O - 0 on success, -1 on error
Fl_Help_View::load(const char *f)// I - Filename to load (may also have target)
{
#ifdef BT_EXTERNAL
  FILE		*fp;		// File to read from
  long		len;		// Length of file
#endif
  char		*target;	// Target in file
  char		*slash;		// Directory separator
  const char	*localname;	// Local filename
  char		error[1024];	// Error buffer
  char		newname[1024];	// New filename buffer


  strlcpy(newname, f, sizeof(newname));
  if ((target = strrchr(newname, '#')) != NULL)
    *target++ = '\0';

  if (link_)
    localname = (*link_)(this, newname);
  else
    localname = filename_;

  if (!localname)
    return (0);

  strlcpy(filename_, newname, sizeof(filename_));
  strlcpy(directory_, newname, sizeof(directory_));

  // Note: We do not support Windows backslashes, since they are illegal
  //       in URLs...
  if ((slash = strrchr(directory_, '/')) == NULL)
    directory_[0] = '\0';
  else if (slash > directory_ && slash[-1] != '/')
    *slash = '\0';

  if (value_ != NULL)
  {
    free((void *)value_);
    value_ = NULL;
  }

  if (strncmp(localname, "ftp:", 4) == 0 ||
      strncmp(localname, "http:", 5) == 0 ||
      strncmp(localname, "https:", 6) == 0 ||
      strncmp(localname, "ipp:", 4) == 0 ||
      strncmp(localname, "mailto:", 7) == 0 ||
      strncmp(localname, "news:", 5) == 0)
  {
    // Remote link wasn't resolved...
    snprintf(error, sizeof(error),
             "Error"
             "

Error

" "

Unable to follow the link \"%s\" - " "no handler exists for this URI scheme.

", localname); value_ = strdup(error); } else { if (strncmp(localname, "file:", 5) == 0) localname += 5; // Adjust for local filename... #ifndef BT_EXTERNAL int i = 0; while (filelist[i].name && strcmp(filelist[i].name, localname)) i++; if (filelist[i].name) { printf("found file\n"); value_ = (char *)calloc(filelist[i].size, 1); strcpy(value_, filelist[i].data); } #else if ((fp = fopen(localname, "rb")) != NULL) { fseek(fp, 0, SEEK_END); len = ftell(fp); rewind(fp); value_ = (char *)calloc(len + 1, 1); fread((void *)value_, 1, len, fp); fclose(fp); } #endif else { snprintf(error, sizeof(error), "Error" "

Error

" "

Unable to follow the link \"%s\" - " "%s.

", localname, strerror(errno)); value_ = strdup(error); } } format(); if (target) topline(target); else topline(0); return (0); } // // 'Fl_Help_View::resize()' - Resize the help widget. // void Fl_Help_View::resize(int xx, // I - New left position int yy, // I - New top position int ww, // I - New width int hh) // I - New height { Fl_Boxtype b = box() ? box() : FL_DOWN_BOX; // Box to draw... Fl_Widget::resize(xx, yy, ww, hh); scrollbar_.resize(x() + w() - 17 - Fl::box_dw(b) + Fl::box_dx(b), y() + Fl::box_dy(b), 17, h() - 17 - Fl::box_dh(b)); hscrollbar_.resize(x() + Fl::box_dx(b), y() + h() - 17 - Fl::box_dh(b) + Fl::box_dy(b), w() - 17 - Fl::box_dw(b), 17); format(); } // // 'Fl_Help_View::topline()' - Set the top line to the named target. // void Fl_Help_View::topline(const char *n) // I - Target name { Fl_Help_Target key, // Target name key *target; // Pointer to matching target if (ntargets_ == 0) return; strlcpy(key.name, n, sizeof(key.name)); target = (Fl_Help_Target *)bsearch(&key, targets_, ntargets_, sizeof(Fl_Help_Target), (compare_func_t)compare_targets); if (target != NULL) topline(target->y); } // // 'Fl_Help_View::topline()' - Set the top line by number. // void Fl_Help_View::topline(int t) // I - Top line number { if (!value_) return; if (size_ < (h() - 24) || t < 0) t = 0; else if (t > size_) t = size_; topline_ = t; scrollbar_.value(topline_, h() - 24, 0, size_); do_callback(); redraw(); } // // 'Fl_Help_View::leftline()' - Set the left position. // void Fl_Help_View::leftline(int l) // I - Left position { if (!value_) return; if (hsize_ < (w() - 24) || l < 0) l = 0; else if (l > hsize_) l = hsize_; leftline_ = l; hscrollbar_.value(leftline_, w() - 24, 0, hsize_); redraw(); } // // 'Fl_Help_View::value()' - Set the help text directly. // void Fl_Help_View::value(const char *v) // I - Text to view { if (!v) return; if (value_ != NULL) free((void *)value_); value_ = strdup(v); format(); set_changed(); topline(0); leftline(0); } // // 'quote_char()' - Return the character code associated with a quoted char. // static int // O - Code or -1 on error quote_char(const char *p) { // I - Quoted string int i; // Looping var static struct { const char *name; int namelen; int code; } *nameptr, // Pointer into name array names[] = { // Quoting names { "Aacute;", 7, 193 }, { "aacute;", 7, 225 }, { "Acirc;", 6, 194 }, { "acirc;", 6, 226 }, { "acute;", 6, 180 }, { "AElig;", 6, 198 }, { "aelig;", 6, 230 }, { "Agrave;", 7, 192 }, { "agrave;", 7, 224 }, { "amp;", 4, '&' }, { "Aring;", 6, 197 }, { "aring;", 6, 229 }, { "Atilde;", 7, 195 }, { "atilde;", 7, 227 }, { "Auml;", 5, 196 }, { "auml;", 5, 228 }, { "brvbar;", 7, 166 }, { "Ccedil;", 7, 199 }, { "ccedil;", 7, 231 }, { "cedil;", 6, 184 }, { "cent;", 5, 162 }, { "copy;", 5, 169 }, { "curren;", 7, 164 }, { "deg;", 4, 176 }, { "divide;", 7, 247 }, { "Eacute;", 7, 201 }, { "eacute;", 7, 233 }, { "Ecirc;", 6, 202 }, { "ecirc;", 6, 234 }, { "Egrave;", 7, 200 }, { "egrave;", 7, 232 }, { "ETH;", 4, 208 }, { "eth;", 4, 240 }, { "Euml;", 5, 203 }, { "euml;", 5, 235 }, { "frac12;", 7, 189 }, { "frac14;", 7, 188 }, { "frac34;", 7, 190 }, { "gt;", 3, '>' }, { "Iacute;", 7, 205 }, { "iacute;", 7, 237 }, { "Icirc;", 6, 206 }, { "icirc;", 6, 238 }, { "iexcl;", 6, 161 }, { "Igrave;", 7, 204 }, { "igrave;", 7, 236 }, { "iquest;", 7, 191 }, { "Iuml;", 5, 207 }, { "iuml;", 5, 239 }, { "laquo;", 6, 171 }, { "lt;", 3, '<' }, { "macr;", 5, 175 }, { "micro;", 6, 181 }, { "middot;", 7, 183 }, { "nbsp;", 5, ' ' }, { "not;", 4, 172 }, { "Ntilde;", 7, 209 }, { "ntilde;", 7, 241 }, { "Oacute;", 7, 211 }, { "oacute;", 7, 243 }, { "Ocirc;", 6, 212 }, { "ocirc;", 6, 244 }, { "Ograve;", 7, 210 }, { "ograve;", 7, 242 }, { "ordf;", 5, 170 }, { "ordm;", 5, 186 }, { "Oslash;", 7, 216 }, { "oslash;", 7, 248 }, { "Otilde;", 7, 213 }, { "otilde;", 7, 245 }, { "Ouml;", 5, 214 }, { "ouml;", 5, 246 }, { "para;", 5, 182 }, { "plusmn;", 7, 177 }, { "pound;", 6, 163 }, { "quot;", 5, '\"' }, { "raquo;", 6, 187 }, { "reg;", 4, 174 }, { "sect;", 5, 167 }, { "shy;", 4, 173 }, { "sup1;", 5, 185 }, { "sup2;", 5, 178 }, { "sup3;", 5, 179 }, { "szlig;", 6, 223 }, { "THORN;", 6, 222 }, { "thorn;", 6, 254 }, { "times;", 6, 215 }, { "Uacute;", 7, 218 }, { "uacute;", 7, 250 }, { "Ucirc;", 6, 219 }, { "ucirc;", 6, 251 }, { "Ugrave;", 7, 217 }, { "ugrave;", 7, 249 }, { "uml;", 4, 168 }, { "Uuml;", 5, 220 }, { "uuml;", 5, 252 }, { "Yacute;", 7, 221 }, { "yacute;", 7, 253 }, { "yen;", 4, 165 }, { "yuml;", 5, 255 }, { "rarr;", 5, 187 } }; if (!strchr(p, ';')) return -1; if (*p == '#') { if (*(p+1) == 'x' || *(p+1) == 'X') return strtol(p+2, NULL, 16); else return atoi(p+1); } for (i = (int)(sizeof(names) / sizeof(names[0])), nameptr = names; i > 0; i --, nameptr ++) if (strncmp(p, nameptr->name, nameptr->namelen) == 0) return nameptr->code; return -1; } // // 'scrollbar_callback()' - A callback for the scrollbar. // static void scrollbar_callback(Fl_Widget *s, void *) { ((Fl_Help_View *)(s->parent()))->topline(int(((Fl_Scrollbar*)s)->value())); } // // 'hscrollbar_callback()' - A callback for the horizontal scrollbar. // static void hscrollbar_callback(Fl_Widget *s, void *) { ((Fl_Help_View *)(s->parent()))->leftline(int(((Fl_Scrollbar*)s)->value())); } // // End of "$Id: Fl_Help_View.cxx 4669 2005-11-28 16:39:57Z matt $". //