diff options
Diffstat (limited to 'src/Fl_Help_View.cxx')
| -rw-r--r-- | src/Fl_Help_View.cxx | 3584 |
1 files changed, 3584 insertions, 0 deletions
diff --git a/src/Fl_Help_View.cxx b/src/Fl_Help_View.cxx new file mode 100644 index 000000000..f5b8063ab --- /dev/null +++ b/src/Fl_Help_View.cxx @@ -0,0 +1,3584 @@ +// +// "$Id: Fl_Help_View.cxx,v 1.1.2.1 2001/09/29 14:38:59 easysw Exp $" +// +// Fl_Help_View widget routines. +// +// Copyright 1997-2001 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 to "fltk-bugs@fltk.org". +// +// Contents: +// +// Fl_Help_View::add_block() - Add a text block to the list. +// Fl_Help_View::add_image() - Add an image to the image cache. +// 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::find_image() - Find an image by name +// 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::load_gif() - Load a GIF image file... +// Fl_Help_View::load_jpeg() - Load a JPEG image file. +// Fl_Help_View::load_png() - Load a PNG image 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. +// Fl_Help_View::compare_blocks() - Compare two blocks. +// gif_read_cmap() - Read the colormap from a GIF file... +// gif_get_block() - Read a GIF data block... +// gif_get_code() - Get a LZW code from the file... +// gif_read_lzw() - Read a byte from the LZW stream... +// gif_read_image() - Read a GIF image stream... +// scrollbar_callback() - A callback for the scrollbar. +// + +// +// Include necessary header files... +// + +#include <FL/Fl_Help_View.H> +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#ifdef HAVE_STRINGS_H +# include <strings.h> +#endif /* HAVE_STRINGS_H */ +#include <errno.h> + +#include <FL/Fl_Image.H> +#include <FL/Fl_Pixmap.H> + +#if defined(WIN32) +# include <io.h> +# include <direct.h> +# define strcasecmp(s,t) stricmp((s), (t)) +# define strncasecmp(s,t,n) strnicmp((s), (t), (n)) +#elif defined(__EMX__) +# define strcasecmp(s,t) stricmp((s), (t)) +# define strncasecmp(s,t,n) strnicmp((s), (t), (n)) +#else +# include <unistd.h> +#endif // WIN32 + +extern "C" +{ +#ifdef HAVE_LIBPNG +# include <zlib.h> +# include <png.h> +#endif // HAVE_LIBPNG + +#ifdef HAVE_LIBJPEG +# include <jpeglib.h> +#endif // HAVE_LIBJPEG +} + +#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 *); +} + +// +// GIF definitions... +// + +#define GIF_INTERLACE 0x40 +#define GIF_COLORMAP 0x80 + +typedef unsigned char gif_cmap_t[256][3]; + + +// +// Local globals... +// + +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 = (Fl_Pixmap *)0; +static int gif_eof = 0; // Did we hit EOF? +static unsigned fltk_colors[] = + { + 0x00000000, + 0xff000000, + 0x00ff0000, + 0xffff0000, + 0x0000ff00, + 0xff00ff00, + 0x00ffff00, + 0xffffff00, + 0x55555500, + 0xc6717100, + 0x71c67100, + 0x8e8e3800, + 0x7171c600, + 0x8e388e00, + 0x388e8e00, + 0xaaaaaa00, + 0x55555500, + 0x55555500, + 0x55555500, + 0x55555500, + 0x55555500, + 0x55555500, + 0x55555500, + 0x55555500, + 0x55555500, + 0x55555500, + 0x55555500, + 0x55555500, + 0x55555500, + 0x55555500, + 0x55555500, + 0x55555500, + 0x00000000, + 0x0d0d0d00, + 0x1a1a1a00, + 0x26262600, + 0x31313100, + 0x3d3d3d00, + 0x48484800, + 0x55555500, + 0x5f5f5f00, + 0x6a6a6a00, + 0x75757500, + 0x80808000, + 0x8a8a8a00, + 0x95959500, + 0xa0a0a000, + 0xaaaaaa00, + 0xb5b5b500, + 0xc0c0c000, + 0xcbcbcb00, + 0xd5d5d500, + 0xe0e0e000, + 0xeaeaea00, + 0xf5f5f500, + 0xffffff00, + 0x00000000, + 0x00240000, + 0x00480000, + 0x006d0000, + 0x00910000, + 0x00b60000, + 0x00da0000, + 0x00ff0000, + 0x3f000000, + 0x3f240000, + 0x3f480000, + 0x3f6d0000, + 0x3f910000, + 0x3fb60000, + 0x3fda0000, + 0x3fff0000, + 0x7f000000, + 0x7f240000, + 0x7f480000, + 0x7f6d0000, + 0x7f910000, + 0x7fb60000, + 0x7fda0000, + 0x7fff0000, + 0xbf000000, + 0xbf240000, + 0xbf480000, + 0xbf6d0000, + 0xbf910000, + 0xbfb60000, + 0xbfda0000, + 0xbfff0000, + 0xff000000, + 0xff240000, + 0xff480000, + 0xff6d0000, + 0xff910000, + 0xffb60000, + 0xffda0000, + 0xffff0000, + 0x00003f00, + 0x00243f00, + 0x00483f00, + 0x006d3f00, + 0x00913f00, + 0x00b63f00, + 0x00da3f00, + 0x00ff3f00, + 0x3f003f00, + 0x3f243f00, + 0x3f483f00, + 0x3f6d3f00, + 0x3f913f00, + 0x3fb63f00, + 0x3fda3f00, + 0x3fff3f00, + 0x7f003f00, + 0x7f243f00, + 0x7f483f00, + 0x7f6d3f00, + 0x7f913f00, + 0x7fb63f00, + 0x7fda3f00, + 0x7fff3f00, + 0xbf003f00, + 0xbf243f00, + 0xbf483f00, + 0xbf6d3f00, + 0xbf913f00, + 0xbfb63f00, + 0xbfda3f00, + 0xbfff3f00, + 0xff003f00, + 0xff243f00, + 0xff483f00, + 0xff6d3f00, + 0xff913f00, + 0xffb63f00, + 0xffda3f00, + 0xffff3f00, + 0x00007f00, + 0x00247f00, + 0x00487f00, + 0x006d7f00, + 0x00917f00, + 0x00b67f00, + 0x00da7f00, + 0x00ff7f00, + 0x3f007f00, + 0x3f247f00, + 0x3f487f00, + 0x3f6d7f00, + 0x3f917f00, + 0x3fb67f00, + 0x3fda7f00, + 0x3fff7f00, + 0x7f007f00, + 0x7f247f00, + 0x7f487f00, + 0x7f6d7f00, + 0x7f917f00, + 0x7fb67f00, + 0x7fda7f00, + 0x7fff7f00, + 0xbf007f00, + 0xbf247f00, + 0xbf487f00, + 0xbf6d7f00, + 0xbf917f00, + 0xbfb67f00, + 0xbfda7f00, + 0xbfff7f00, + 0xff007f00, + 0xff247f00, + 0xff487f00, + 0xff6d7f00, + 0xff917f00, + 0xffb67f00, + 0xffda7f00, + 0xffff7f00, + 0x0000bf00, + 0x0024bf00, + 0x0048bf00, + 0x006dbf00, + 0x0091bf00, + 0x00b6bf00, + 0x00dabf00, + 0x00ffbf00, + 0x3f00bf00, + 0x3f24bf00, + 0x3f48bf00, + 0x3f6dbf00, + 0x3f91bf00, + 0x3fb6bf00, + 0x3fdabf00, + 0x3fffbf00, + 0x7f00bf00, + 0x7f24bf00, + 0x7f48bf00, + 0x7f6dbf00, + 0x7f91bf00, + 0x7fb6bf00, + 0x7fdabf00, + 0x7fffbf00, + 0xbf00bf00, + 0xbf24bf00, + 0xbf48bf00, + 0xbf6dbf00, + 0xbf91bf00, + 0xbfb6bf00, + 0xbfdabf00, + 0xbfffbf00, + 0xff00bf00, + 0xff24bf00, + 0xff48bf00, + 0xff6dbf00, + 0xff91bf00, + 0xffb6bf00, + 0xffdabf00, + 0xffffbf00, + 0x0000ff00, + 0x0024ff00, + 0x0048ff00, + 0x006dff00, + 0x0091ff00, + 0x00b6ff00, + 0x00daff00, + 0x00ffff00, + 0x3f00ff00, + 0x3f24ff00, + 0x3f48ff00, + 0x3f6dff00, + 0x3f91ff00, + 0x3fb6ff00, + 0x3fdaff00, + 0x3fffff00, + 0x7f00ff00, + 0x7f24ff00, + 0x7f48ff00, + 0x7f6dff00, + 0x7f91ff00, + 0x7fb6ff00, + 0x7fdaff00, + 0x7fffff00, + 0xbf00ff00, + 0xbf24ff00, + 0xbf48ff00, + 0xbf6dff00, + 0xbf91ff00, + 0xbfb6ff00, + 0xbfdaff00, + 0xbfffff00, + 0xff00ff00, + 0xff24ff00, + 0xff48ff00, + 0xff6dff00, + 0xff91ff00, + 0xffb6ff00, + 0xffdaff00, + 0xffffff00 + }; + + +// +// Local functions... +// + +static int gif_read_cmap(FILE *fp, int ncolors, gif_cmap_t cmap); +static int gif_get_block(FILE *fp, unsigned char *buffer); +static int gif_get_code (FILE *fp, int code_size, int first_time); +static int gif_read_lzw(FILE *fp, int first_time, int input_code_size); +static int gif_read_image(FILE *fp, Fl_Help_Image *img, gif_cmap_t cmap, + int interlace); +static void scrollbar_callback(Fl_Widget *s, void *); + + +// +// '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_; + temp->start = s; + temp->x = xx; + temp->y = yy; + temp->w = ww; + temp->h = hh; + temp->border = border; + nblocks_ ++; + + return (temp); +} + + +// +// 'Fl_Help_View::add_image()' - Add an image to the image cache. +// + +Fl_Help_Image * // O - Image or NULL if not found +Fl_Help_View::add_image(const char *name, // I - Path of image + const char *wattr, // I - Width attribute + const char *hattr, // I - Height attribute + int make) // I - Make the image? +{ + Fl_Help_Image *img, // New image + *orig; // Original image + FILE *fp; // File pointer + unsigned char header[16]; // First 16 bytes of file + int status; // Status of load... + const char *localname; // Local filename + char dir[1024]; // Current directory + char temp[1024], // Temporary filename + *tempptr; // Pointer into temporary name + int width, // Desired width of image + height; // Desired height of image + + + // See if the image has already been loaded... + if ((img = find_image(name, wattr, hattr)) != (Fl_Help_Image *)0) + { + // Make the image if needed... + if (!img->image) + img->image = new Fl_RGB_Image(img->data, img->w, img->h, img->d); + + return (img); + } + + // See if the image exists with the default size info... + orig = find_image(name, "", ""); + + // Allocate memory as needed... + if (aimage_ == nimage_) + { + aimage_ += 16; + + if (aimage_ == 16) + image_ = (Fl_Help_Image *)malloc(sizeof(Fl_Help_Image) * aimage_); + else + image_ = (Fl_Help_Image *)realloc(image_, sizeof(Fl_Help_Image) * aimage_); + } + + img = image_ + nimage_; + img->name = strdup(name); + img->copy = 0; + + if (!orig) + { + // See if the image can be found... + if (strchr(directory_, ':') != NULL && strchr(name, ':') == NULL) + { + if (name[0] == '/') + { + strcpy(temp, directory_); + if ((tempptr = strrchr(strchr(directory_, ':') + 3, '/')) != NULL) + strcpy(tempptr, name); + else + strcat(temp, name); + } + else + sprintf(temp, "%s/%s", directory_, name); + + if (link_) + localname = (*link_)(temp); + else + localname = temp; + } + else if (name[0] != '/' && strchr(name, ':') == NULL) + { + if (directory_[0]) + sprintf(temp, "%s/%s", directory_, name); + else + { + getcwd(dir, sizeof(dir)); + sprintf(temp, "file:%s/%s", dir, name); + } + + if (link_) + localname = (*link_)(temp); + else + localname = temp; + } + else if (link_) + localname = (*link_)(name); + else + localname = name; + + if (!localname) + return ((Fl_Help_Image *)0); + + if (strncmp(localname, "file:", 5) == 0) + localname += 5; + + // Figure out the file type... + if ((fp = fopen(localname, "rb")) == NULL) + return ((Fl_Help_Image *)0); + + if (fread(header, 1, sizeof(header), fp) == 0) + return ((Fl_Help_Image *)0); + + rewind(fp); + + // Load the image as appropriate... + if (memcmp(header, "GIF87a", 6) == 0 || + memcmp(header, "GIF89a", 6) == 0) + status = load_gif(img, fp); + #ifdef HAVE_LIBPNG + else if (memcmp(header, "\211PNG", 4) == 0) + status = load_png(img, fp); + #endif // HAVE_LIBPNG + #ifdef HAVE_LIBJPEG + else if (memcmp(header, "\377\330\377", 3) == 0 && // Start-of-Image + header[3] >= 0xe0 && header[3] <= 0xef) // APPn + status = load_jpeg(img, fp); + #endif // HAVE_LIBJPEG + else + status = 0; + + fclose(fp); + + if (!status) + { + free(img->name); + return ((Fl_Help_Image *)0); + } + + img->wattr[0] = '\0'; + img->hattr[0] = '\0'; + + nimage_ ++; + + // Allocate memory as needed for the new copy... + if (aimage_ == nimage_) + { + aimage_ += 16; + image_ = (Fl_Help_Image *)realloc(image_, sizeof(Fl_Help_Image) * aimage_); + } + + orig = image_ + nimage_ - 1; + img = image_ + nimage_; + img->name = strdup(name); + } + +// printf("orig->data = %p, width = %d, height = %d\n", orig->data, +// orig->w, orig->h); + + // Copy image data from original image... + img->data = orig->data; + img->w = orig->w; + img->h = orig->h; + img->d = orig->d; + img->copy = 1; + + // Figure out the size of the image... + if (wattr[0]) + { + if (wattr[strlen(wattr) - 1] == '%') + width = atoi(wattr) * (w() - 24) / 100; + else + width = atoi(wattr); + } + else + width = 0; + + if (hattr[0]) + { + if (hattr[strlen(hattr) - 1] == '%') + height = atoi(hattr) * h() / 100; + else + height = atoi(hattr); + } + else + height = 0; + + if (width == 0 && height == 0) + { + // Use image size... + width = img->w; + height = img->h; + } + else if (width == 0) + // Scale width to height + width = img->w * height / img->h; + else if (height == 0) + // Scale height to width + height = img->h * width / img->w; + + // Scale the image as needed... + if (width != img->w && height != img->h) + { + unsigned char *scaled, // Scaled image data + *sptr, // Source image data pointer + *dptr; // Destination image data pointer + int sy, // Source coordinates + dx, dy, // Destination coordinates + xerr, yerr, // X & Y errors + xmod, ymod, // X & Y moduli + xstep, ystep; // X & Y step increments + + + xmod = img->w % width; + xstep = (img->w / width) * img->d; + ymod = img->h % height; + ystep = img->h / height; + + if ((scaled = (unsigned char *)malloc(width * height * img->d)) != NULL) + { + img->copy = 0; + + // Scale the image... + for (dy = height, sy = 0, yerr = height / 2, dptr = scaled; dy > 0; dy --) + { + for (dx = width, xerr = width / 2, + sptr = img->data + sy * img->w * img->d; + dx > 0; + dx --) + { + *dptr++ = sptr[0]; + if (img->d > 1) + { + *dptr++ = sptr[1]; + *dptr++ = sptr[2]; + } + + sptr += xstep; + xerr -= xmod; + if (xerr <= 0) + { + xerr += width; + sptr += img->d; + } + } + + sy += ystep; + yerr -= ymod; + if (yerr <= 0) + { + yerr += height; + sy ++; + } + } + + // Finally, copy the new size and data to the image structure... + if (!orig) + free(img->data); + + img->w = width; + img->h = height; + img->data = scaled; + } + } + + strncpy(img->wattr, wattr, sizeof(img->wattr) - 1); + img->wattr[sizeof(img->wattr) - 1] = '\0'; + strncpy(img->hattr, hattr, sizeof(img->hattr) - 1); + img->hattr[sizeof(img->hattr) - 1] = '\0'; + + if (make) + img->image = new Fl_RGB_Image(img->data, img->w, img->h, img->d); + else + img->image = (Fl_Image *)0; + + nimage_ ++; + +// printf("img->data = %p, width = %d, height = %d\n", img->data, +// img->w, img->h); + + return (img); +} + + +// +// '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; + + strncpy(temp->filename, n, sizeof(temp->filename)); + temp->filename[sizeof(temp->filename) - 1] = '\0'; + + if ((target = strrchr(temp->filename, '#')) != NULL) + { + *target++ = '\0'; + strncpy(temp->name, target, sizeof(temp->name)); + temp->name[sizeof(temp->name) - 1] = '\0'; + } + 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; + strncpy(temp->name, n, sizeof(temp->name)); + temp->name[sizeof(temp->name) - 1] = '\0'; + + 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, size; // 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... + Fl_Color tc, c; // Table/cell background color + + + // Draw the scrollbar and box first... + if (scrollbar_.visible()) + { + draw_child(scrollbar_); + draw_box(b, x(), y(), w() - 17, h(), bgcolor_); + } + else + draw_box(b, x(), y(), w(), h(), bgcolor_); + + if (!value_) + return; + + // Clip the drawing to the inside of the box... + fl_push_clip(x() + 4, y() + 4, w() - 28, h() - 8); + fl_color(textcolor_); + + tc = c = bgcolor_; + + // Draw all visible blocks... + for (i = 0, block = blocks_; i < nblocks_ && (block->y - topline_) < h(); i ++, block ++) + if ((block->y + block->h) >= topline_) + { + line = 0; + xx = block->line[line]; + yy = block->y - topline_; + hh = 0; + pre = 0; + head = 0; + needspace = 0; + + initfont(font, size); + + 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(), yy + y()); + + xx += ww; + if ((size + 2) > hh) + hh = size + 2; + + needspace = 0; + } + else if (pre) + { + while (isspace(*ptr)) + { + if (*ptr == '\n') + { + *s = '\0'; + s = buf; + + fl_draw(buf, xx + x(), yy + y()); + + if (line < 31) + line ++; + xx = block->line[line]; + yy += hh; + hh = size + 2; + } + else if (*ptr == '\t') + { + // Do tabs every 8 columns... + while (((s - buf) & 7)) + *s++ = ' '; + } + else + *s++ = ' '; + + if ((size + 2) > hh) + hh = size + 2; + + ptr ++; + } + + if (s > buf) + { + *s = '\0'; + s = buf; + + fl_draw(buf, xx + x(), yy + y()); + xx += (int)fl_width(buf); + } + + needspace = 0; + } + else + { + s = buf; + + while (isspace(*ptr)) + ptr ++; + } + } + + if (*ptr == '<') + { + ptr ++; + 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(), block->w + x(), + yy + y()); + + 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; + size = textsize_ + '7' - buf[1]; + } + else if (strcasecmp(buf, "DT") == 0) + { + font = textfont_ | FL_ITALIC; + size = textsize_; + } + else if (strcasecmp(buf, "PRE") == 0) + { + font = FL_COURIER; + size = textsize_; + pre = 1; + } + + if (strcasecmp(buf, "LI") == 0) + { + fl_font(FL_SYMBOL, size); + fl_draw("\267", xx - size + x(), yy + y()); + } + + pushfont(font, size); + + if (c != bgcolor_) + { + fl_color(c); + fl_rectf(block->x + x() - 4, + block->y - topline_ + y() - size - 3, + block->w - block->x + 7, block->h + size - 5); + fl_color(textcolor_); + } + } + else if (strcasecmp(buf, "A") == 0 && + get_attr(attrs, "HREF", attr, sizeof(attr)) != NULL) + fl_color(linkcolor_); + else if (strcasecmp(buf, "/A") == 0) + fl_color(textcolor_); + else if (strcasecmp(buf, "B") == 0) + pushfont(font |= FL_BOLD, size); + else if (strcasecmp(buf, "TABLE") == 0) + tc = get_color(get_attr(attrs, "BGCOLOR", attr, sizeof(attr)), bgcolor_); + else if (strcasecmp(buf, "TD") == 0 || + strcasecmp(buf, "TH") == 0) + { + if (tolower(buf[1]) == 'h') + pushfont(font |= FL_BOLD, size); + else + pushfont(font = textfont_, size); + + c = get_color(get_attr(attrs, "BGCOLOR", attr, sizeof(attr)), tc); + + if (c != bgcolor_) + { + fl_color(c); + fl_rectf(block->x + x() - 4, + block->y - topline_ + y() - size - 3, + block->w - block->x + 7, block->h + size - 5); + fl_color(textcolor_); + } + + if (block->border) + fl_rect(block->x + x() - 4, + block->y - topline_ + y() - size - 3, + block->w - block->x + 7, block->h + size - 5); + } + else if (strcasecmp(buf, "I") == 0) + pushfont(font |= FL_ITALIC, size); + else if (strcasecmp(buf, "CODE") == 0) + pushfont(font = FL_COURIER, size); + else if (strcasecmp(buf, "KBD") == 0) + pushfont(font = FL_COURIER_BOLD, size); + else if (strcasecmp(buf, "VAR") == 0) + pushfont(font = FL_COURIER_ITALIC, size); + 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, "/I") == 0 || + strcasecmp(buf, "/CODE") == 0 || + strcasecmp(buf, "/KBD") == 0 || + strcasecmp(buf, "/VAR") == 0) + popfont(font, size); + else if (strcasecmp(buf, "/TABLE") == 0) + tc = c = bgcolor_; + else if (strcasecmp(buf, "/TD") == 0 || + strcasecmp(buf, "/TH") == 0) + c = tc; + else if (strcasecmp(buf, "/PRE") == 0) + { + popfont(font, size); + pre = 0; + } + else if (strcasecmp(buf, "IMG") == 0) + { + Fl_Help_Image *img = (Fl_Help_Image *)0; + int width = 16; + int height = 24; + char wattr[8], hattr[8]; + + + get_attr(attrs, "WIDTH", wattr, sizeof(wattr)); + get_attr(attrs, "HEIGHT", hattr, sizeof(hattr)); + + if (get_attr(attrs, "SRC", attr, sizeof(attr))) + if ((img = add_image(attr, wattr, hattr)) != NULL) + { + if (!img->image) + img = (Fl_Help_Image *)0; + } + + if (img) + { + width = img->w; + height = img->h; + } + else 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->image->draw(xx + x(), + yy + y() - fl_height() + fl_descent() + 2); + else + broken_image->draw(xx + x(), + 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(), yy + y()); + + if (line < 31) + line ++; + xx = block->line[line]; + yy += hh; + hh = size + 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 ++; + + if (strncasecmp(ptr, "amp;", 4) == 0) + { + *s++ = '&'; + ptr += 4; + } + else if (strncasecmp(ptr, "lt;", 3) == 0) + { + *s++ = '<'; + ptr += 3; + } + else if (strncasecmp(ptr, "gt;", 3) == 0) + { + *s++ = '>'; + ptr += 3; + } + else if (strncasecmp(ptr, "nbsp;", 5) == 0) + { + *s++ = ' '; + ptr += 5; + } + else if (strncasecmp(ptr, "copy;", 5) == 0) + { + *s++ = '\251'; + ptr += 5; + } + else if (strncasecmp(ptr, "reg;", 4) == 0) + { + *s++ = '\256'; + ptr += 4; + } + else if (strncasecmp(ptr, "quot;", 5) == 0) + { + *s++ = '\"'; + ptr += 5; + } + + if ((size + 2) > hh) + hh = size + 2; + } + else + { + *s++ = *ptr++; + + if ((size + 2) > hh) + hh = size + 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(), yy + y()); + } + + fl_pop_clip(); +} + + +// +// 'Fl_Help_View::find_image()' - Find an image by name +// + +Fl_Help_Image * // O - Image or NULL if not found +Fl_Help_View::find_image(const char *name, // I - Path and name of image + const char *wattr, // I - Width attribute of image + const char *hattr) // I - Height attribute of image +{ + int i; // Looping var + Fl_Help_Image *img; // Current image + + + for (i = nimage_, img = image_; i > 0; i --, img ++) + if (strcmp(img->name, name) == 0 && + strcmp(img->wattr, wattr) == 0 && + strcmp(img->hattr, hattr) == 0) + return (img); + + return ((Fl_Help_Image *)0); +} + + +// +// 'Fl_Help_View::format()' - Format the help text. +// + +void +Fl_Help_View::format() +{ + int i; // Looping var + Fl_Help_Block *block, // Current block + *cell; // Current table cell + int 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 + link[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, size; // Current font and size + unsigned char border; // Draw border? + int align, // Current alignment + newalign, // New alignment + head, // In the <HEAD> section? + pre, // <PRE> text? + needspace; // Do we need whitespace? + int table_width; // Width of table + int column, // Current table column number + columns[MAX_COLUMNS]; + // Column widths + + + // Reset state variables... + nblocks_ = 0; + nlinks_ = 0; + ntargets_ = 0; + size_ = 0; + bgcolor_ = color(); + textcolor_ = textcolor(); + linkcolor_ = selection_color(); + + strcpy(title_, "Untitled"); + + if (!value_) + return; + + // Flush images that are scaled by percentage... + for (i = 0; i < nimage_; i ++) + if (strchr(image_[i].wattr, '%') != NULL || + strchr(image_[i].hattr, '%') != NULL) + { + // Flush this one... + free(image_[i].name); + free(image_[i].data); + delete image_[i].image; + nimage_ --; + if (i < nimage_) + memcpy(image_ + i, image_ + i + 1, (nimage_ - i) * sizeof(Fl_Help_Image)); + i --; + } + + // Setup for formatting... + initfont(font, size); + + line = 0; + links = 0; + xx = 4; + yy = size + 2; + ww = 0; + column = 0; + border = 0; + hh = 0; + block = add_block(value_, xx, yy, w() - 24, 0); + row = 0; + head = 0; + pre = 0; + align = LEFT; + newalign = LEFT; + needspace = 0; + link[0] = '\0'; + + for (ptr = value_, s = buf; *ptr;) + { + if ((*ptr == '<' || isspace(*ptr)) && s > buf) + { + if (!head && !pre) + { + // Check width... + *s = '\0'; + ww = (int)fl_width(buf); + + 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 (link[0]) + add_link(link, xx, yy - size, ww, size); + + xx += ww; + if ((size + 2) > hh) + hh = size + 2; + + needspace = 0; + } + else if (pre) + { + // Handle preformatted text... + while (isspace(*ptr)) + { + if (*ptr == '\n') + { + if (link[0]) + add_link(link, xx, yy - hh, ww, hh); + + line = do_align(block, line, xx, newalign, links); + xx = block->x; + yy += hh; + block->h += hh; + hh = size + 2; + } + + if ((size + 2) > hh) + hh = size + 2; + + ptr ++; + } + + needspace = 0; + } + else + { + // Handle normal text or stuff in the <HEAD> section... + while (isspace(*ptr)) + ptr ++; + } + + s = buf; + } + + if (*ptr == '<') + { + start = ptr; + ptr ++; + 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 - size - 2); + else if (get_attr(attrs, "HREF", attr, sizeof(attr)) != NULL) + { + strncpy(link, attr, sizeof(link) - 1); + link[sizeof(link) - 1] = '\0'; + } + } + else if (strcasecmp(buf, "/A") == 0) + link[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 += size + 2; + xx += 4 * size; + } + else if (strcasecmp(buf, "TABLE") == 0) + { + if (get_attr(attrs, "BORDER", attr, sizeof(attr))) + border = atoi(attr); + else + border = 0; + + block->h += size + 2; + + format_table(&table_width, columns, start); + + column = 0; + } + + if (tolower(buf[0]) == 'h' && isdigit(buf[1])) + { + font = FL_HELVETICA_BOLD; + size = textsize_ + '7' - buf[1]; + } + else if (strcasecmp(buf, "DT") == 0) + { + font = textfont_ | FL_ITALIC; + size = textsize_; + } + else if (strcasecmp(buf, "PRE") == 0) + { + font = FL_COURIER; + size = textsize_; + pre = 1; + } + else + { + font = textfont_; + size = textsize_; + } + + pushfont(font, size); + + yy = block->y + block->h; + hh = 0; + + if ((tolower(buf[0]) == 'h' && isdigit(buf[1])) || + strcasecmp(buf, "DD") == 0 || + strcasecmp(buf, "DT") == 0 || + strcasecmp(buf, "P") == 0) + yy += size + 2; + else if (strcasecmp(buf, "HR") == 0) + { + hh += 2 * size; + yy += size; + } + + if (row) + block = add_block(start, xx, yy, block->w, 0); + else + block = add_block(start, xx, yy, w() - 24, 0); + + needspace = 0; + line = 0; + + if (strcasecmp(buf, "CENTER") == 0) + newalign = align = CENTER; + else + newalign = get_align(attrs, align); + } + 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, "/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, "/DL") == 0) + { + xx -= 4 * size; + block->h += size + 2; + } + else if (strcasecmp(buf, "/TABLE") == 0) + block->h += size + 2; + else if (strcasecmp(buf, "/PRE") == 0) + { + pre = 0; + hh = 0; + } + else if (strcasecmp(buf, "/CENTER") == 0) + align = LEFT; + + popfont(font, size); + + while (isspace(*ptr)) + ptr ++; + + block->h += hh; + yy += hh; + + if (tolower(buf[2]) == 'l') + yy += size + 2; + + if (row) + block = add_block(ptr, xx, yy, block->w, 0); + else + block = add_block(ptr, xx, yy, w() - 24, 0); + + needspace = 0; + hh = 0; + line = 0; + newalign = align; + } + 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->h = yy - block->y + 2; + + for (cell = blocks_ + row + 1; cell < block; cell ++) + cell->h = block->h; + } + + yy = block->y + block->h - 4; + hh = 0; + block = add_block(start, xx, yy, w() - 24, 0); + row = block - blocks_; + needspace = 0; + column = 0; + line = 0; + } + 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->h = yy - block->y + 2; + + for (cell = blocks_ + row + 1; cell < block; cell ++) + cell->h = block->h; + + yy = block->y + block->h - 4; + block = add_block(start, xx, yy, w() - 24, 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 = textfont_ | FL_BOLD; + else + font = textfont_; + + size = textsize_; + + xx = blocks_[row].x + size + 3; + 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 = 0; i < colspan; i ++) + ww += columns[column + i]; + + if (block->end == block->start && nblocks_ > 1) + { + nblocks_ --; + block --; + } + + pushfont(font, size); + + 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); + + column ++; + } + else if ((strcasecmp(buf, "/TD") == 0 || + strcasecmp(buf, "/TH") == 0) && row) + popfont(font, size); + else if (strcasecmp(buf, "B") == 0) + pushfont(font |= FL_BOLD, size); + else if (strcasecmp(buf, "I") == 0) + pushfont(font |= FL_ITALIC, size); + else if (strcasecmp(buf, "CODE") == 0) + pushfont(font = FL_COURIER, size); + else if (strcasecmp(buf, "KBD") == 0) + pushfont(font = FL_COURIER_BOLD, size); + else if (strcasecmp(buf, "VAR") == 0) + pushfont(font = FL_COURIER_ITALIC, size); + else if (strcasecmp(buf, "/B") == 0 || + strcasecmp(buf, "/I") == 0 || + strcasecmp(buf, "/CODE") == 0 || + strcasecmp(buf, "/KBD") == 0 || + strcasecmp(buf, "/VAR") == 0) + popfont(font, size); + else if (strcasecmp(buf, "IMG") == 0) + { + Fl_Help_Image *img = (Fl_Help_Image *)0; + int width = 16; + int height = 24; + + + get_attr(attrs, "WIDTH", wattr, sizeof(wattr)); + get_attr(attrs, "HEIGHT", hattr, sizeof(hattr)); + + if (get_attr(attrs, "SRC", attr, sizeof(attr))) + if ((img = add_image(attr, wattr, hattr)) != (Fl_Help_Image *)0 && + img->image == NULL) + img = (Fl_Help_Image *)0; + + if (img) + { + width = img->w; + height = img->h; + } + else if (get_attr(attrs, "ALT", attr, sizeof(attr)) == NULL) + strcpy(attr, "IMG"); + + ww = width; + + 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 (link[0]) + add_link(link, xx, yy - height, ww, height); + + xx += ww; + if ((height + 2) > hh) + hh = height + 2; + + needspace = 0; + } + } + else if (*ptr == '\n' && pre) + { + if (link[0]) + add_link(link, xx, yy - hh, ww, hh); + + 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 ++; + + if (strncasecmp(ptr, "amp;", 4) == 0) + { + *s++ = '&'; + ptr += 4; + } + else if (strncasecmp(ptr, "lt;", 3) == 0) + { + *s++ = '<'; + ptr += 3; + } + else if (strncasecmp(ptr, "gt;", 3) == 0) + { + *s++ = '>'; + ptr += 3; + } + else if (strncasecmp(ptr, "nbsp;", 5) == 0) + { + *s++ = '\240'; + ptr += 5; + } + else if (strncasecmp(ptr, "copy;", 5) == 0) + { + *s++ = '\251'; + ptr += 5; + } + else if (strncasecmp(ptr, "reg;", 4) == 0) + { + *s++ = '\256'; + ptr += 4; + } + else if (strncasecmp(ptr, "quot;", 5) == 0) + { + *s++ = '\"'; + ptr += 5; + } + + if ((size + 2) > hh) + hh = size + 2; + } + else + { + if (s < (buf + sizeof(buf) - 1)) + *s++ = *ptr++; + else + ptr ++; + + if ((size + 2) > hh) + hh = size + 2; + } + } + + if (s > buf && !pre && !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 (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 (link[0]) + add_link(link, xx, yy - size, ww, size); + + xx += ww; + if ((size + 2) > hh) + hh = size + 2; + + needspace = 0; + } + + block->end = ptr; + size_ = yy + hh; + + if (ntargets_ > 1) + qsort(targets_, ntargets_, sizeof(Fl_Help_Target), + (compare_func_t)compare_targets); + + if (nblocks_ > 1) + qsort(blocks_, nblocks_, sizeof(Fl_Help_Block), + (compare_func_t)compare_blocks); + + if (size_ < (h() - 8)) + scrollbar_.hide(); + else + scrollbar_.show(); + + topline(topline_); +} + + +// +// '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, // <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, size; // 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; + + // 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; + size = textsize_ + '7' - buf[1]; + } + else if (strcasecmp(buf, "DT") == 0) + { + font = textfont_ | FL_ITALIC; + size = textsize_; + } + else if (strcasecmp(buf, "PRE") == 0) + { + font = FL_COURIER; + size = textsize_; + pre = 1; + } + else if (strcasecmp(buf, "LI") == 0) + { + width += 4 * size; + font = textfont_; + size = textsize_; + } + else + { + font = textfont_; + size = textsize_; + } + + pushfont(font, size); + } + 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, size); + } + 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 = textfont_ | FL_BOLD; + else + font = textfont_; + + size = textsize_; + + pushfont(font, size); + + if (get_attr(attrs, "WIDTH", attr, sizeof(attr)) != NULL) + { + max_width = atoi(attr); + + if (attr[strlen(attr) - 1] == '%') + max_width = max_width * w() / 100; + } + 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, size); + } + else if (strcasecmp(buf, "B") == 0) + pushfont(font |= FL_BOLD, size); + else if (strcasecmp(buf, "I") == 0) + pushfont(font |= FL_ITALIC, size); + else if (strcasecmp(buf, "CODE") == 0) + pushfont(font = FL_COURIER, size); + else if (strcasecmp(buf, "KBD") == 0) + pushfont(font = FL_COURIER_BOLD, size); + else if (strcasecmp(buf, "VAR") == 0) + pushfont(font = FL_COURIER_ITALIC, size); + else if (strcasecmp(buf, "/B") == 0 || + strcasecmp(buf, "/I") == 0 || + strcasecmp(buf, "/CODE") == 0 || + strcasecmp(buf, "/KBD") == 0 || + strcasecmp(buf, "/VAR") == 0) + popfont(font, size); + else if (strcasecmp(buf, "IMG") == 0 && incell) + { + Fl_Help_Image *img = (Fl_Help_Image *)0; + + + get_attr(attrs, "WIDTH", wattr, sizeof(wattr)); + get_attr(attrs, "HEIGHT", hattr, sizeof(hattr)); + + if (get_attr(attrs, "SRC", attr, sizeof(attr))) + if ((img = add_image(attr, wattr, hattr)) != (Fl_Help_Image *)0 && + img->image == NULL) + img = (Fl_Help_Image *)0; + + if (img) + temp_width = img->w; + else + temp_width = 16; + + if (temp_width > minwidths[column]) + minwidths[column] = temp_width; + + width += temp_width; + 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 ++; + + if (strncasecmp(ptr, "amp;", 4) == 0) + { + *s++ = '&'; + ptr += 4; + } + else if (strncasecmp(ptr, "lt;", 3) == 0) + { + *s++ = '<'; + ptr += 3; + } + else if (strncasecmp(ptr, "gt;", 3) == 0) + { + *s++ = '>'; + ptr += 3; + } + else if (strncasecmp(ptr, "nbsp;", 5) == 0) + { + *s++ = '\240'; + ptr += 5; + } + else if (strncasecmp(ptr, "copy;", 5) == 0) + { + *s++ = '\251'; + ptr += 5; + } + else if (strncasecmp(ptr, "reg;", 4) == 0) + { + *s++ = '\256'; + ptr += 4; + } + else if (strncasecmp(ptr, "quot;", 5) == 0) + { + *s++ = '\"'; + ptr += 5; + } + } + 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))) + { + if (attr[strlen(attr) - 1] == '%') + *table_width = atoi(attr) * w() / 100; + else + *table_width = atoi(attr); + } + else + *table_width = 0; + +// printf("num_columns = %d, table_width = %d\n", num_columns, *table_width); + + if (num_columns == 0) + return; + + // Add up the widths... + for (column = 0, width = 0; column < num_columns; column ++) + width += columns[column]; + +// 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]); + + // Adjust the width if needed... + int scale_width = *table_width; + + if (scale_width == 0 && width > w()) + scale_width = width; + + if (width > scale_width) + { + *table_width = 0; + + for (column = 0; column < num_columns; column ++) + { + if (width > 0) + { + temp_width = scale_width * columns[column] / width; + + if (temp_width < minwidths[column]) + temp_width = minwidths[column]; + } + else + temp_width = minwidths[column]; + + width -= columns[column]; + scale_width -= temp_width; + columns[column] = temp_width; + (*table_width) += temp_width; + } + } + else if (*table_width == 0) + *table_width = width; + +// printf("FINAL table_width = %d\n", *table_width); +// for (column = 0; column < num_columns; column ++) +// printf(" columns[%d] = %d\n", column, columns[column]); +} + + +// +// '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 rgb, r, g, b; // RGB values + + + if (!n) + return (c); + + if (n[0] == '#') + { + // Do hex color lookup + rgb = strtol(n + 1, NULL, 16); + + r = rgb >> 16; + g = (rgb >> 8) & 255; + b = rgb & 255; + + if (r == g && g == b) + return (fl_gray_ramp(FL_NUM_GRAY * r / 256)); + else + return (fl_color_cube((FL_NUM_RED - 1) * r / 255, + (FL_NUM_GREEN - 1) * g / 255, + (FL_NUM_BLUE - 1) * b / 255)); + } + else if (strcasecmp(n, "black") == 0) + return (FL_BLACK); + else if (strcasecmp(n, "red") == 0) + return (FL_RED); + else if (strcasecmp(n, "green") == 0) + return (fl_color_cube(0, 4, 0)); + else if (strcasecmp(n, "yellow") == 0) + return (FL_YELLOW); + else if (strcasecmp(n, "blue") == 0) + return (FL_BLUE); + else if (strcasecmp(n, "magenta") == 0 || strcasecmp(n, "fuchsia") == 0) + return (FL_MAGENTA); + else if (strcasecmp(n, "cyan") == 0 || strcasecmp(n, "aqua") == 0) + return (FL_CYAN); + else if (strcasecmp(n, "white") == 0) + return (FL_WHITE); + else if (strcasecmp(n, "gray") == 0 || strcasecmp(n, "grey") == 0) + return (FL_GRAY); + else if (strcasecmp(n, "lime") == 0) + return (FL_GREEN); + else if (strcasecmp(n, "maroon") == 0) + return (fl_color_cube(2, 0, 0)); + else if (strcasecmp(n, "navy") == 0) + return (fl_color_cube(0, 0, 2)); + else if (strcasecmp(n, "olive") == 0) + return (fl_color_cube(2, 4, 0)); + else if (strcasecmp(n, "purple") == 0) + return (fl_color_cube(2, 0, 2)); + else if (strcasecmp(n, "silver") == 0) + return (FL_LIGHT2); + else if (strcasecmp(n, "teal") == 0) + return (fl_color_cube(0, 4, 2)); + else + return (c); +} + + +// +// '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 *link; // 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(); + yy = Fl::event_y() - y() + topline_; + break; + + default : + return (Fl_Group::handle(event)); + } + + // Handle mouse clicks on links... + for (i = nlinks_, link = links_; i > 0; i --, link ++) + if (xx >= link->x && xx < link->w && + yy >= link->y && yy < link->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); + + strncpy(target, link->name, sizeof(target) - 1); + target[sizeof(target) - 1] = '\0'; + + set_changed(); + + if (strcmp(link->filename, filename_) != 0 && link->filename[0]) + { + char dir[1024]; // Current directory + char temp[1024], // Temporary filename + *tempptr; // Pointer into temporary filename + + + if (strchr(directory_, ':') != NULL && strchr(link->filename, ':') == NULL) + { + if (link->filename[0] == '/') + { + strcpy(temp, directory_); + if ((tempptr = strrchr(strchr(directory_, ':') + 3, '/')) != NULL) + strcpy(tempptr, link->filename); + else + strcat(temp, link->filename); + } + else + sprintf(temp, "%s/%s", directory_, link->filename); + + load(temp); + } + else if (link->filename[0] != '/' && strchr(link->filename, ':') == NULL) + { + if (directory_[0]) + sprintf(temp, "%s/%s", directory_, link->filename); + else + { + getcwd(dir, sizeof(dir)); + sprintf(temp, "file:%s/%s", dir, link->filename); + } + + load(temp); + } + else + load(link->filename); + } + else if (target[0]) + topline(target); + else + topline(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) +{ + link_ = (Fl_Help_Func *)0; + + filename_[0] = '\0'; + value_ = NULL; + + ablocks_ = 0; + nblocks_ = 0; + blocks_ = (Fl_Help_Block *)0; + + nimage_ = 0; + aimage_ = 0; + image_ = (Fl_Help_Image *)0; + + if (!broken_image) + broken_image = new Fl_Pixmap((char **)broken_xpm); + + alinks_ = 0; + nlinks_ = 0; + links_ = (Fl_Help_Link *)0; + + atargets_ = 0; + ntargets_ = 0; + targets_ = (Fl_Help_Target *)0; + + nfonts_ = 0; + textfont_ = FL_TIMES; + textsize_ = 12; + + topline_ = 0; + size_ = 0; + + color(FL_WHITE); + textcolor(FL_BLACK); + selection_color(FL_BLUE); + + scrollbar_.value(0, hh, 0, 1); + scrollbar_.step(8.0); + scrollbar_.show(); + scrollbar_.callback(scrollbar_callback); + + end(); +} + + +// +// 'Fl_Help_View::~Fl_Help_View()' - Destroy a Fl_Help_View widget. +// + +Fl_Help_View::~Fl_Help_View() +{ + int i; // Looping var + Fl_Help_Image *img; // Current image + + + if (nblocks_) + free(blocks_); + if (nlinks_) + free(links_); + if (ntargets_) + free(targets_); + if (value_) + free((void *)value_); + if (image_) + { + for (i = nimage_, img = image_; i > 0; i --, img ++) + { + delete img->image; + if (!img->copy) + free(img->data); + free(img->name); + } + } +} + + +// +// '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) +{ + FILE *fp; // File to read from + long len; // Length of file + char *target; // Target in file + char *slash; // Directory separator + const char *localname; // Local filename + char error[1024]; // Error buffer + + + strcpy(filename_, f); + strcpy(directory_, filename_); + + if ((slash = strrchr(directory_, '/')) == NULL) + directory_[0] = '\0'; + else if (slash > directory_ && slash[-1] != '/') + *slash = '\0'; + + if ((target = strrchr(filename_, '#')) != NULL) + *target++ = '\0'; + + if (link_) + localname = (*link_)(filename_); + else + localname = filename_; + + if (localname != NULL && + (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)) + localname = NULL; // Remote link wasn't resolved... + else if (localname != NULL && + strncmp(localname, "file:", 5) == 0) + localname += 5; // Adjust for local filename... + + if (value_ != NULL) + { + free((void *)value_); + value_ = NULL; + } + + if (localname) + { + if ((fp = fopen(localname, "rb")) != NULL) + { + fseek(fp, 0, SEEK_END); + len = ftell(fp); + rewind(fp); + + value_ = (const char *)calloc(len + 1, 1); + fread((void *)value_, 1, len, fp); + fclose(fp); + } + else + { + sprintf(error, "%s: %s\n", localname, strerror(errno)); + value_ = strdup(error); + } + } + else + { + sprintf(error, "%s: %s\n", filename_, strerror(errno)); + value_ = strdup(error); + } + + format(); + + if (target) + topline(target); + else + topline(0); + + return (0); +} + + +// +// 'Fl_Help_View::load_gif()' - Load a GIF image file... +// + +int // O - 0 = success, -1 = fail +Fl_Help_View::load_gif(Fl_Help_Image *img,// I - Image pointer + FILE *fp) // I - File to load from +{ + unsigned char buf[1024]; // Input buffer + gif_cmap_t cmap; // Colormap + int ncolors, // Bits per pixel + transparent; // Transparent color index + + + // Read the header; we already know it is a GIF file... + fread(buf, 13, 1, fp); + + img->w = (buf[7] << 8) | buf[6]; + img->h = (buf[9] << 8) | buf[8]; + ncolors = 2 << (buf[10] & 0x07); + + if (buf[10] & GIF_COLORMAP) + if (!gif_read_cmap(fp, ncolors, cmap)) + return (0); + + transparent = -1; + + for (;;) + { + switch (getc(fp)) + { + case ';' : // End of image + return (0); // Early end of file + + case '!' : // Extension record + buf[0] = getc(fp); + if (buf[0] == 0xf9) // Graphic Control Extension + { + gif_get_block(fp, buf); + if (buf[0] & 1) // Get transparent color index + transparent = buf[3]; + } + + while (gif_get_block(fp, buf) != 0); + break; + + case ',' : // Image data + fread(buf, 9, 1, fp); + + if (buf[8] & GIF_COLORMAP) + { + ncolors = 2 << (buf[8] & 0x07); + + if (!gif_read_cmap(fp, ncolors, cmap)) + return (0); + } + + if (transparent >= 0) + { + unsigned rgba = fltk_colors[bgcolor_]; + + + // Map transparent color to background color... + cmap[transparent][0] = rgba >> 24; + cmap[transparent][1] = rgba >> 16; + cmap[transparent][2] = rgba >> 8; + } + + img->w = (buf[5] << 8) | buf[4]; + img->h = (buf[7] << 8) | buf[6]; + img->d = 3; + img->data = (unsigned char *)malloc(img->w * img->h * img->d); + if (img->data == NULL) + return (0); + + return (gif_read_image(fp, img, cmap, buf[8] & GIF_INTERLACE)); + } + } +} + + +#ifdef HAVE_LIBJPEG +// +// 'Fl_Help_View::load_jpeg()' - Load a JPEG image file. +// + +int // O - 0 = success, -1 = fail +Fl_Help_View::load_jpeg(Fl_Help_Image *img, // I - Image pointer + FILE *fp) // I - File to load from +{ + struct jpeg_decompress_struct cinfo; // Decompressor info + struct jpeg_error_mgr jerr; // Error handler info + JSAMPROW row; // Sample row pointer + + + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_decompress(&cinfo); + jpeg_stdio_src(&cinfo, fp); + jpeg_read_header(&cinfo, 1); + + cinfo.quantize_colors = 0; + cinfo.out_color_space = JCS_RGB; + cinfo.out_color_components = 3; + cinfo.output_components = 3; + + jpeg_calc_output_dimensions(&cinfo); + + img->w = cinfo.output_width; + img->h = cinfo.output_height; + img->d = cinfo.output_components; + img->data = (unsigned char *)malloc(img->w * img->h * img->d); + + if (img->data == NULL) + { + jpeg_destroy_decompress(&cinfo); + return (0); + } + + jpeg_start_decompress(&cinfo); + + while (cinfo.output_scanline < cinfo.output_height) + { + row = (JSAMPROW)(img->data + + cinfo.output_scanline * cinfo.output_width * + cinfo.output_components); + jpeg_read_scanlines(&cinfo, &row, (JDIMENSION)1); + } + + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + + return (1); +} +#endif // HAVE_LIBJPEG + + +#ifdef HAVE_LIBPNG +// +// 'Fl_Help_View::load_png()' - Load a PNG image file. +// + +int // O - 0 = success, -1 = fail +Fl_Help_View::load_png(Fl_Help_Image *img,// I - Image pointer + FILE *fp) // I - File to read from +{ + int i; // Looping var + png_structp pp; // PNG read pointer + png_infop info; // PNG info pointers + png_bytep *rows; // PNG row pointers + png_color_16 bg; // Background color + + + // Setup the PNG data structures... + pp = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + info = png_create_info_struct(pp); + + // Initialize the PNG read "engine"... + png_init_io(pp, fp); + + // Get the image dimensions and convert to grayscale or RGB... + png_read_info(pp, info); + + if (info->color_type == PNG_COLOR_TYPE_PALETTE) + png_set_expand(pp); + + if (info->color_type == PNG_COLOR_TYPE_GRAY) + img->d = 1; + else + img->d = 3; + + img->w = (int)info->width; + img->h = (int)info->height; + img->data = (unsigned char *)malloc(img->w * img->h * 3); + + if (info->bit_depth < 8) + { + png_set_packing(pp); + png_set_expand(pp); + + if (info->valid & PNG_INFO_sBIT) + png_set_shift(pp, &(info->sig_bit)); + } + else if (info->bit_depth == 16) + png_set_strip_16(pp); + +#ifdef HAVE_PNG_GET_VALID + // Handle transparency... + if (png_get_valid(pp, info, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha(pp); +#endif // HAVE_PNG_GET_VALID + + // Background color... + unsigned rgba = fltk_colors[bgcolor_]; + + bg.red = 65535 * (rgba >> 24) / 255; + bg.green = 65535 * ((rgba >> 16) & 255) / 255; + bg.blue = 65535 * ((rgba >> 8) & 255) / 255; + + png_set_background(pp, &bg, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0); + + // Allocate pointers... + rows = (png_bytep *)calloc(info->height, sizeof(png_bytep)); + + for (i = 0; i < (int)info->height; i ++) + if (info->color_type == PNG_COLOR_TYPE_GRAY) + rows[i] = img->data + i * img->w; + else + rows[i] = img->data + i * img->w * 3; + + // Read the image, handling interlacing as needed... + for (i = png_set_interlace_handling(pp); i > 0; i --) + png_read_rows(pp, rows, NULL, img->h); + + // Free memory and return... + free(rows); + + png_read_end(pp, info); + png_read_destroy(pp, info, NULL); + + return (1); +} +#endif // HAVE_LIBPNG + + +// +// '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_Widget::resize(xx, yy, ww, hh); + scrollbar_.resize(xx + ww - 17, yy, 17, hh); + + 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; + + strncpy(key.name, n, sizeof(key.name) - 1); + key.name[sizeof(key.name) - 1] = '\0'; + + 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() - 8) || t < 0) + t = 0; + else if (t > size_) + t = size_; + + topline_ = t; + + scrollbar_.value(topline_, h(), 0, size_); + + do_callback(); + clear_changed(); + + 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); +} + + +// +// 'Fl_Help_View::compare_blocks()' - Compare two blocks. +// + +int // O - Result of comparison +Fl_Help_View::compare_blocks(const void *a, // I - First block + const void *b) // I - Second block +{ + return (((Fl_Help_Block *)a)->y - ((Fl_Help_Block *)b)->y); +} + + +// +// 'gif_read_cmap()' - Read the colormap from a GIF file... +// + +static int // O - -1 = error, 0 = success +gif_read_cmap(FILE *fp, // I - File to read from + int ncolors, // I - Number of colors + gif_cmap_t cmap) // O - Colormap +{ + // Read the colormap... + if (fread(cmap, 3, ncolors, fp) < (size_t)ncolors) + return (0); + + return (1); +} + + +// +// 'gif_get_block()' - Read a GIF data block... +// + +static int // O - Number characters read +gif_get_block(FILE *fp, // I - File to read from + unsigned char *buf) // I - Input buffer +{ + int count; // Number of character to read + + + // Read the count byte followed by the data from the file... + if ((count = getc(fp)) == EOF) + { + gif_eof = 1; + return (-1); + } + else if (count == 0) + gif_eof = 1; + else if (fread(buf, 1, count, fp) < (size_t)count) + { + gif_eof = 1; + return (-1); + } + else + gif_eof = 0; + + return (count); +} + + +// +// 'gif_get_code()' - Get a LZW code from the file... +// + +static int // O - LZW code +gif_get_code(FILE *fp, // I - File to read from + int code_size, // I - Size of code in bits + int first_time) // I - 1 = first time, 0 = not first time +{ + unsigned i, j, // Looping vars + ret; // Return value + int count; // Number of bytes read + static unsigned char buf[280]; // Input buffer + static unsigned curbit, // Current bit + lastbit, // Last bit in buffer + done, // Done with this buffer? + last_byte; // Last byte in buffer + static unsigned bits[8] = // Bit masks for codes + { + 0x01, 0x02, 0x04, 0x08, + 0x10, 0x20, 0x40, 0x80 + }; + + + if (first_time) + { + // Just initialize the input buffer... + curbit = 0; + lastbit = 0; + done = 0; + + return (0); + } + + + if ((curbit + code_size) >= lastbit) + { + // Don't have enough bits to hold the code... + if (done) + return (-1); // Sorry, no more... + + // Move last two bytes to front of buffer... + if (last_byte > 1) + { + buf[0] = buf[last_byte - 2]; + buf[1] = buf[last_byte - 1]; + last_byte = 2; + } + else if (last_byte == 1) + { + buf[0] = buf[last_byte - 1]; + last_byte = 1; + } + + // Read in another buffer... + if ((count = gif_get_block (fp, buf + last_byte)) <= 0) + { + // Whoops, no more data! + done = 1; + return (-1); + } + + // Update buffer state... + curbit = (curbit - lastbit) + 8 * last_byte; + last_byte += count; + lastbit = last_byte * 8; + } + + ret = 0; + for (ret = 0, i = curbit + code_size - 1, j = code_size; + j > 0; + i --, j --) + ret = (ret << 1) | ((buf[i / 8] & bits[i & 7]) != 0); + + curbit += code_size; + + return ret; +} + + +// +// 'gif_read_lzw()' - Read a byte from the LZW stream... +// + +static int // I - Byte from stream +gif_read_lzw(FILE *fp, // I - File to read from + int first_time, // I - 1 = first time, 0 = not first time + int input_code_size) // I - Code size in bits +{ + int i, // Looping var + code, // Current code + incode; // Input code + static short fresh = 0, // 1 = empty buffers + code_size, // Current code size + set_code_size, // Initial code size set + max_code, // Maximum code used + max_code_size, // Maximum code size + firstcode, // First code read + oldcode, // Last code read + clear_code, // Clear code for LZW input + end_code, // End code for LZW input + table[2][4096], // String table + stack[8192], // Output stack + *sp; // Current stack pointer + + + if (first_time) + { + // Setup LZW state... + set_code_size = input_code_size; + code_size = set_code_size + 1; + clear_code = 1 << set_code_size; + end_code = clear_code + 1; + max_code_size = 2 * clear_code; + max_code = clear_code + 2; + + // Initialize input buffers... + gif_get_code(fp, 0, 1); + + // Wipe the decompressor table... + fresh = 1; + + for (i = 0; i < clear_code; i ++) + { + table[0][i] = 0; + table[1][i] = i; + } + + for (; i < 4096; i ++) + table[0][i] = table[1][0] = 0; + + sp = stack; + + return (0); + } + else if (fresh) + { + fresh = 0; + + do + firstcode = oldcode = gif_get_code(fp, code_size, 0); + while (firstcode == clear_code); + + return (firstcode); + } + + if (sp > stack) + return (*--sp); + + while ((code = gif_get_code (fp, code_size, 0)) >= 0) + { + if (code == clear_code) + { + for (i = 0; i < clear_code; i ++) + { + table[0][i] = 0; + table[1][i] = i; + } + + for (; i < 4096; i ++) + table[0][i] = table[1][i] = 0; + + code_size = set_code_size + 1; + max_code_size = 2 * clear_code; + max_code = clear_code + 2; + + sp = stack; + + firstcode = oldcode = gif_get_code(fp, code_size, 0); + + return (firstcode); + } + else if (code == end_code) + { + unsigned char buf[260]; + + + if (!gif_eof) + while (gif_get_block(fp, buf) > 0); + + return (-2); + } + + incode = code; + + if (code >= max_code) + { + *sp++ = firstcode; + code = oldcode; + } + + while (code >= clear_code) + { + *sp++ = table[1][code]; + if (code == table[0][code]) + return (255); + + code = table[0][code]; + } + + *sp++ = firstcode = table[1][code]; + code = max_code; + + if (code < 4096) + { + table[0][code] = oldcode; + table[1][code] = firstcode; + max_code ++; + + if (max_code >= max_code_size && max_code_size < 4096) + { + max_code_size *= 2; + code_size ++; + } + } + + oldcode = incode; + + if (sp > stack) + return (*--sp); + } + + return (code); +} + + +// +// 'gif_read_image()' - Read a GIF image stream... +// + +static int // I - 0 = success, -1 = failure +gif_read_image(FILE *fp, // I - Input file + Fl_Help_Image *img, // I - Image pointer + gif_cmap_t cmap, // I - Colormap + int interlace) // I - Non-zero = interlaced image +{ + unsigned char code_size, // Code size + *temp; // Current pixel + int xpos, // Current X position + ypos, // Current Y position + pass; // Current pass + int pixel; // Current pixel + static int xpasses[4] = { 8, 8, 4, 2 }, + ypasses[5] = { 0, 4, 2, 1, 999999 }; + + + xpos = 0; + ypos = 0; + pass = 0; + code_size = getc(fp); + + if (gif_read_lzw(fp, 1, code_size) < 0) + return (0); + + temp = img->data; + + while ((pixel = gif_read_lzw(fp, 0, code_size)) >= 0) + { + temp[0] = cmap[pixel][0]; + + if (img->d > 1) + { + temp[1] = cmap[pixel][1]; + temp[2] = cmap[pixel][2]; + } + + xpos ++; + temp += img->d; + if (xpos == img->w) + { + xpos = 0; + + if (interlace) + { + ypos += xpasses[pass]; + temp += (xpasses[pass] - 1) * img->w * img->d; + + if (ypos >= img->h) + { + pass ++; + + ypos = ypasses[pass]; + temp = img->data + ypos * img->w * img->d; + } + } + else + ypos ++; + } + + if (ypos >= img->h) + break; + } + + 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())); +} + + +// +// End of "$Id: Fl_Help_View.cxx,v 1.1.2.1 2001/09/29 14:38:59 easysw Exp $". +// |
