fltk 1.3.0rc3
About: FLTK (Fast Light Tool Kit) is a cross-platform C++ GUI toolkit for UNIX/Linux (X11), Microsoft Windows, and MacOS X. Release candidate.
  SfR Fresh Dox: fltk-1.3.0rc3-source.tar.gz ("inofficial" and yet experimental doxygen-generated source code documentation)  

Fl_Help_View.cxx

Go to the documentation of this file.
00001 //
00002 // "$Id: Fl_Help_View.cxx 8063 2010-12-19 21:20:10Z matt $"
00003 //
00004 // Fl_Help_View widget routines.
00005 //
00006 // Copyright 1997-2010 by Easy Software Products.
00007 // Image support by Matthias Melcher, Copyright 2000-2009.
00008 //
00009 // This library is free software; you can redistribute it and/or
00010 // modify it under the terms of the GNU Library General Public
00011 // License as published by the Free Software Foundation; either
00012 // version 2 of the License, or (at your option) any later version.
00013 //
00014 // This library is distributed in the hope that it will be useful,
00015 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017 // Library General Public License for more details.
00018 //
00019 // You should have received a copy of the GNU Library General Public
00020 // License along with this library; if not, write to the Free Software
00021 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
00022 // USA.
00023 //
00024 // Please report all bugs and problems on the following page:
00025 //
00026 //     http://www.fltk.org/str.php
00027 //
00028 // Contents:
00029 //
00030 //   Fl_Help_View::add_block()       - Add a text block to the list.
00031 //   Fl_Help_View::add_link()        - Add a new link to the list.
00032 //   Fl_Help_View::add_target()      - Add a new target to the list.
00033 //   Fl_Help_View::compare_targets() - Compare two targets.
00034 //   Fl_Help_View::do_align()        - Compute the alignment for a line in
00035 //                                     a block.
00036 //   Fl_Help_View::draw()            - Draw the Fl_Help_View widget.
00037 //   Fl_Help_View::format()          - Format the help text.
00038 //   Fl_Help_View::format_table()    - Format a table...
00039 //   Fl_Help_View::free_data()       - Free memory used for the document.
00040 //   Fl_Help_View::get_align()       - Get an alignment attribute.
00041 //   Fl_Help_View::get_attr()        - Get an attribute value from the string.
00042 //   Fl_Help_View::get_color()       - Get an alignment attribute.
00043 //   Fl_Help_View::handle()          - Handle events in the widget.
00044 //   Fl_Help_View::Fl_Help_View()    - Build a Fl_Help_View widget.
00045 //   Fl_Help_View::~Fl_Help_View()   - Destroy a Fl_Help_View widget.
00046 //   Fl_Help_View::load()            - Load the specified file.
00047 //   Fl_Help_View::resize()          - Resize the help widget.
00048 //   Fl_Help_View::topline()         - Set the top line to the named target.
00049 //   Fl_Help_View::topline()         - Set the top line by number.
00050 //   Fl_Help_View::value()           - Set the help text directly.
00051 //   scrollbar_callback()            - A callback for the scrollbar.
00052 //
00053 
00054 //
00055 // Include necessary header files...
00056 //
00057 
00058 #include <FL/Fl_Help_View.H>
00059 #include <FL/Fl_Window.H>
00060 #include <FL/Fl_Pixmap.H>
00061 #include <FL/x.H>
00062 #include <stdio.h>
00063 #include <stdlib.h>
00064 #include <FL/fl_utf8.h>
00065 #include <FL/filename.H>        // fl_open_uri()
00066 #include "flstring.h"
00067 #include <ctype.h>
00068 #include <errno.h>
00069 #include <math.h>
00070 
00071 #if defined(WIN32) && ! defined(__CYGWIN__)
00072 #  include <io.h>
00073 #  include <direct.h>
00074 #else
00075 #  include <unistd.h>
00076 #endif // WIN32
00077 
00078 #define MAX_COLUMNS     200
00079 
00080 
00081 //
00082 // Typedef the C API sort function type the only way I know how...
00083 //
00084 
00085 extern "C"
00086 {
00087   typedef int (*compare_func_t)(const void *, const void *);
00088 }
00089 
00090 
00091 //
00092 // Local functions...
00093 //
00094 
00095 static int      quote_char(const char *);
00096 static void     scrollbar_callback(Fl_Widget *s, void *);
00097 static void     hscrollbar_callback(Fl_Widget *s, void *);
00098 
00099 //
00100 // global flag for image loading (see get_image).
00101 //
00102 
00103 static char initial_load = 0;
00104 
00105 //
00106 // Broken image...
00107 //
00108 
00109 static const char *broken_xpm[] =
00110                 {
00111                   "16 24 4 1",
00112                   "@ c #000000",
00113                   "  c #ffffff",
00114                   "+ c none",
00115                   "x c #ff0000",
00116                   // pixels
00117                   "@@@@@@@+++++++++",
00118                   "@    @++++++++++",
00119                   "@   @+++++++++++",
00120                   "@   @++@++++++++",
00121                   "@    @@+++++++++",
00122                   "@     @+++@+++++",
00123                   "@     @++@@++++@",
00124                   "@ xxx  @@  @++@@",
00125                   "@  xxx    xx@@ @",
00126                   "@   xxx  xxx   @",
00127                   "@    xxxxxx    @",
00128                   "@     xxxx     @",
00129                   "@    xxxxxx    @",
00130                   "@   xxx  xxx   @",
00131                   "@  xxx    xxx  @",
00132                   "@ xxx      xxx @",
00133                   "@              @",
00134                   "@              @",
00135                   "@              @",
00136                   "@              @",
00137                   "@              @",
00138                   "@              @",
00139                   "@              @",
00140                   "@@@@@@@@@@@@@@@@",
00141                   NULL
00142                 };
00143 
00144 static Fl_Pixmap broken_image(broken_xpm);
00145 
00146 //
00147 // Simple margin stack for Fl_Help_View::format()...
00148 //
00149 
00150 struct fl_margins {
00151   int depth_;
00152   int margins_[100];
00153 
00154   fl_margins() { clear();  }
00155 
00156   int clear() {
00157 //    puts("fl_margins::clear()");
00158 
00159     depth_ = 0;
00160     return margins_[0] = 4;
00161   }
00162 
00163   int current() { return margins_[depth_]; }
00164 
00165   int pop() {
00166 //    printf("fl_margins::pop(): depth_=%d, xx=%d\n", depth_,
00167 //           depth_ > 0 ? margins_[depth_ - 1] : 4);
00168 
00169     if (depth_ > 0) {
00170       depth_ --;
00171       return margins_[depth_];
00172     } else return 4;
00173   }
00174 
00175   int push(int indent) {
00176     int xx;
00177 
00178     xx = margins_[depth_] + indent;
00179 
00180 //    printf("fl_margins::push(indent=%d): depth_=%d, xx=%d\n", indent,
00181 //           depth_ + 1, xx);
00182 
00183     if (depth_ < 99) {
00184       depth_ ++;
00185       margins_[depth_] = xx;
00186     }
00187 
00188     return xx;
00189   }
00190 };
00191 
00192 //
00193 // All the stuff needed to implement text selection in Fl_Help_View
00194 //
00195 
00196 /* matt:
00197  * We are trying to keep binary compatibility with previous versions
00198  * of FLTK. This means that we are limited to adding static variables
00199  * only to not enlarge the Fl_Help_View class. Lucky for us, only one
00200  * text can be selected system wide, so we can remember the selection
00201  * in a single set of variables.
00202  *
00203  * Still to do:
00204  * - &word; style characters mess up our count inside a word boundary
00205  * - we can only select words, no individual characters
00206  * - no dragging of the selection into another widget
00207  * - selection must be cleared if another widget get focus!
00208  * - write a comment for every new function
00209  */
00210 
00211 /*
00212 The following functions are also used to draw stuff and should be replaced with
00213 local copies that are much faster when merely counting:
00214 
00215 fl_color(Fl_Color);
00216 fl_rectf(int, int, int, int);
00217 fl_push_clip(int, int, int, int);
00218 fl_xyline(int, int, int);
00219 fl_rect()
00220 fl_line()
00221 img->draw()
00222 */
00223 
00224 // We don't put the offscreen buffer in the help view class because
00225 // we'd need to include x.H in the header...
00226 static Fl_Offscreen fl_help_view_buffer;
00227 int Fl_Help_View::selection_first = 0;
00228 int Fl_Help_View::selection_last = 0;
00229 int Fl_Help_View::selection_push_first = 0;
00230 int Fl_Help_View::selection_push_last = 0;
00231 int Fl_Help_View::selection_drag_first = 0;
00232 int Fl_Help_View::selection_drag_last = 0;
00233 int Fl_Help_View::selected = 0;
00234 int Fl_Help_View::draw_mode = 0;
00235 int Fl_Help_View::mouse_x = 0;
00236 int Fl_Help_View::mouse_y = 0;
00237 int Fl_Help_View::current_pos = 0;
00238 Fl_Help_View *Fl_Help_View::current_view = 0L;
00239 Fl_Color Fl_Help_View::hv_selection_color;
00240 Fl_Color Fl_Help_View::hv_selection_text_color;
00241 
00242 /*
00243  * Limitation: if a word contains &code; notations, we will calculate a wrong length.
00244  *
00245  * This function must be optimized for speed!
00246  */
00247 void Fl_Help_View::hv_draw(const char *t, int x, int y)
00248 {
00249   if (selected && current_view==this && current_pos<selection_last && current_pos>=selection_first) {
00250     Fl_Color c = fl_color();
00251     fl_color(hv_selection_color);
00252     int w = (int)fl_width(t);
00253     if (current_pos+(int)strlen(t)<selection_last) 
00254       w += (int)fl_width(' ');
00255     fl_rectf(x, y+fl_descent()-fl_height(), w, fl_height());
00256     fl_color(hv_selection_text_color);
00257     fl_draw(t, x, y);
00258     fl_color(c);
00259   } else {
00260     fl_draw(t, x, y);
00261   }
00262   if (draw_mode) {
00263     int w = (int)fl_width(t);
00264     if (mouse_x>=x && mouse_x<x+w) {
00265       if (mouse_y>=y-fl_height()+fl_descent()&&mouse_y<=y+fl_descent()) {
00266         int f = current_pos;
00267         int l = f+strlen(t); // use 'quote_char' to calculate the true length of the HTML string
00268         if (draw_mode==1) {
00269           selection_push_first = f;
00270           selection_push_last = l;
00271         } else {
00272           selection_drag_first = f;
00273           selection_drag_last = l;
00274         }
00275       }
00276     }
00277   }
00278 }
00279 
00280 
00282 Fl_Help_Block *                                 // O - Pointer to new block
00283 Fl_Help_View::add_block(const char   *s,        // I - Pointer to start of block text
00284                         int           xx,       // I - X position of block
00285                         int           yy,       // I - Y position of block
00286                         int           ww,       // I - Right margin of block
00287                         int           hh,       // I - Height of block
00288                         unsigned char border)   // I - Draw border?
00289 {
00290   Fl_Help_Block *temp;                          // New block
00291 
00292 
00293 //  printf("add_block(s = %p, xx = %d, yy = %d, ww = %d, hh = %d, border = %d)\n",
00294 //         s, xx, yy, ww, hh, border);
00295 
00296   if (nblocks_ >= ablocks_)
00297   {
00298     ablocks_ += 16;
00299 
00300     if (ablocks_ == 16)
00301       blocks_ = (Fl_Help_Block *)malloc(sizeof(Fl_Help_Block) * ablocks_);
00302     else
00303       blocks_ = (Fl_Help_Block *)realloc(blocks_, sizeof(Fl_Help_Block) * ablocks_);
00304   }
00305 
00306   temp = blocks_ + nblocks_;
00307   memset(temp, 0, sizeof(Fl_Help_Block));
00308   temp->start   = s;
00309   temp->end     = s;
00310   temp->x       = xx;
00311   temp->y       = yy;
00312   temp->w       = ww;
00313   temp->h       = hh;
00314   temp->border  = border;
00315   temp->bgcolor = bgcolor_;
00316   nblocks_ ++;
00317 
00318   return (temp);
00319 }
00320 
00321 
00323 void Fl_Help_View::add_link(const char *n,      // I - Name of link
00324                       int        xx,    // I - X position of link
00325                       int        yy,    // I - Y position of link
00326                       int        ww,    // I - Width of link text
00327                       int        hh)    // I - Height of link text
00328 {
00329   Fl_Help_Link  *temp;                  // New link
00330   char          *target;                // Pointer to target name
00331 
00332 
00333   if (nlinks_ >= alinks_)
00334   {
00335     alinks_ += 16;
00336 
00337     if (alinks_ == 16)
00338       links_ = (Fl_Help_Link *)malloc(sizeof(Fl_Help_Link) * alinks_);
00339     else
00340       links_ = (Fl_Help_Link *)realloc(links_, sizeof(Fl_Help_Link) * alinks_);
00341   }
00342 
00343   temp = links_ + nlinks_;
00344 
00345   temp->x       = xx;
00346   temp->y       = yy;
00347   temp->w       = xx + ww;
00348   temp->h       = yy + hh;
00349 
00350   strlcpy(temp->filename, n, sizeof(temp->filename));
00351 
00352   if ((target = strrchr(temp->filename, '#')) != NULL)
00353   {
00354     *target++ = '\0';
00355     strlcpy(temp->name, target, sizeof(temp->name));
00356   }
00357   else
00358     temp->name[0] = '\0';
00359 
00360   nlinks_ ++;
00361 }
00362 
00363 
00365 void Fl_Help_View::add_target(const char *n,    // I - Name of target
00366                               int        yy)    // I - Y position of target
00367 {
00368   Fl_Help_Target        *temp;                  // New target
00369 
00370 
00371   if (ntargets_ >= atargets_)
00372   {
00373     atargets_ += 16;
00374 
00375     if (atargets_ == 16)
00376       targets_ = (Fl_Help_Target *)malloc(sizeof(Fl_Help_Target) * atargets_);
00377     else
00378       targets_ = (Fl_Help_Target *)realloc(targets_, sizeof(Fl_Help_Target) * atargets_);
00379   }
00380 
00381   temp = targets_ + ntargets_;
00382 
00383   temp->y = yy;
00384   strlcpy(temp->name, n, sizeof(temp->name));
00385 
00386   ntargets_ ++;
00387 }
00388 
00390 int                                                     // O - Result of comparison
00391 Fl_Help_View::compare_targets(const Fl_Help_Target *t0, // I - First target
00392                              const Fl_Help_Target *t1)  // I - Second target
00393 {
00394   return (strcasecmp(t0->name, t1->name));
00395 }
00396 
00398 int                                             // O - New line
00399 Fl_Help_View::do_align(Fl_Help_Block *block,    // I - Block to add to
00400                       int          line,        // I - Current line
00401                       int          xx,          // I - Current X position
00402                       int          a,           // I - Current alignment
00403                       int          &l)          // IO - Starting link
00404 {
00405   int   offset;                                 // Alignment offset
00406 
00407 
00408   switch (a)
00409   {
00410     case RIGHT :        // Right align
00411         offset = block->w - xx;
00412         break;
00413     case CENTER :       // Center
00414         offset = (block->w - xx) / 2;
00415         break;
00416     default :           // Left align
00417         offset = 0;
00418         break;
00419   }
00420 
00421   block->line[line] = block->x + offset;
00422 
00423   if (line < 31)
00424     line ++;
00425 
00426   while (l < nlinks_)
00427   {
00428     links_[l].x += offset;
00429     links_[l].w += offset;
00430     l ++;
00431   }
00432 
00433   return (line);
00434 }
00435 
00437 void
00438 Fl_Help_View::draw()
00439 {
00440   int                   i;              // Looping var
00441   const Fl_Help_Block   *block;         // Pointer to current block
00442   const char            *ptr,           // Pointer to text in block
00443                         *attrs;         // Pointer to start of element attributes
00444   char                  *s,             // Pointer into buffer
00445                         buf[1024],      // Text buffer
00446                         attr[1024];     // Attribute buffer
00447   int                   xx, yy, ww, hh; // Current positions and sizes
00448   int                   line;           // Current line
00449   Fl_Font               font;
00450   Fl_Fontsize           fsize;          // Current font and size
00451   Fl_Color              fcolor;         // current font color 
00452   int                   head, pre,      // Flags for text
00453                         needspace;      // Do we need whitespace?
00454   Fl_Boxtype            b = box() ? box() : FL_DOWN_BOX;
00455                                         // Box to draw...
00456   int                   underline,      // Underline text?
00457                         xtra_ww;        // Extra width for underlined space between words
00458 
00459   // Draw the scrollbar(s) and box first...
00460   ww = w();
00461   hh = h();
00462   i  = 0;
00463 
00464   draw_box(b, x(), y(), ww, hh, bgcolor_);
00465 
00466   if ( hscrollbar_.visible() || scrollbar_.visible() ) {
00467     int scrollsize = scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size();
00468     int hor_vis = hscrollbar_.visible();
00469     int ver_vis = scrollbar_.visible();
00470     // Scrollbar corner
00471     int scorn_x = x() + ww - (ver_vis?scrollsize:0) - Fl::box_dw(b) + Fl::box_dx(b);
00472     int scorn_y = y() + hh - (hor_vis?scrollsize:0) - Fl::box_dh(b) + Fl::box_dy(b);
00473     if ( hor_vis ) {
00474       if ( hscrollbar_.h() != scrollsize ) {            // scrollsize changed?
00475         hscrollbar_.resize(x(), scorn_y, scorn_x - x(), scrollsize);
00476         init_sizes();
00477       }
00478       draw_child(hscrollbar_);
00479       hh -= scrollsize;
00480     }
00481     if ( ver_vis ) {
00482       if ( scrollbar_.w() != scrollsize ) {             // scrollsize changed?
00483         scrollbar_.resize(scorn_x, y(), scrollsize, scorn_y - y());
00484         init_sizes();
00485       }
00486       draw_child(scrollbar_);
00487       ww -= scrollsize;
00488     }
00489     if ( hor_vis && ver_vis ) {
00490       // Both scrollbars visible? Draw little gray box in corner
00491       fl_color(FL_GRAY);
00492       fl_rectf(scorn_x, scorn_y, scrollsize, scrollsize);
00493     }
00494   }
00495 
00496   if (!value_)
00497     return;
00498 
00499   if (current_view == this && selected) {
00500     hv_selection_color      = FL_SELECTION_COLOR;
00501     hv_selection_text_color = fl_contrast(textcolor_, FL_SELECTION_COLOR);
00502   }
00503   current_pos = 0;
00504 
00505   // Clip the drawing to the inside of the box...
00506   fl_push_clip(x() + Fl::box_dx(b), y() + Fl::box_dy(b),
00507                ww - Fl::box_dw(b), hh - Fl::box_dh(b));
00508   fl_color(textcolor_);
00509 
00510   // Draw all visible blocks...
00511   for (i = 0, block = blocks_; i < nblocks_; i ++, block ++)
00512     if ((block->y + block->h) >= topline_ && block->y < (topline_ + h()))
00513     {
00514       line      = 0;
00515       xx        = block->line[line];
00516       yy        = block->y - topline_;
00517       hh        = 0;
00518       pre       = 0;
00519       head      = 0;
00520       needspace = 0;
00521       underline = 0;
00522 
00523       initfont(font, fsize, fcolor);
00524 
00525       for (ptr = block->start, s = buf; ptr < block->end;)
00526       {
00527         if ((*ptr == '<' || isspace((*ptr)&255)) && s > buf)
00528         {
00529           if (!head && !pre)
00530           {
00531             // Check width...
00532             *s = '\0';
00533             s  = buf;
00534             ww = (int)fl_width(buf);
00535 
00536             if (needspace && xx > block->x)
00537               xx += (int)fl_width(' ');
00538 
00539             if ((xx + ww) > block->w)
00540             {
00541               if (line < 31)
00542                 line ++;
00543               xx = block->line[line];
00544               yy += hh;
00545               hh = 0;
00546             }
00547 
00548             hv_draw(buf, xx + x() - leftline_, yy + y());
00549             if (underline) {
00550               xtra_ww = isspace((*ptr)&255)?(int)fl_width(' '):0;
00551               fl_xyline(xx + x() - leftline_, yy + y() + 1,
00552                         xx + x() - leftline_ + ww + xtra_ww);
00553             }
00554             current_pos = ptr-value_;
00555 
00556             xx += ww;
00557             if ((fsize + 2) > hh)
00558               hh = fsize + 2;
00559 
00560             needspace = 0;
00561           }
00562           else if (pre)
00563           {
00564             while (isspace((*ptr)&255))
00565             {
00566               if (*ptr == '\n')
00567               {
00568                 *s = '\0';
00569                 s = buf;
00570 
00571                 hv_draw(buf, xx + x() - leftline_, yy + y());
00572                 if (underline) fl_xyline(xx + x() - leftline_, yy + y() + 1,
00573                                          xx + x() - leftline_ +
00574                                              (int)fl_width(buf));
00575 
00576                 current_pos = ptr-value_;
00577                 if (line < 31)
00578                   line ++;
00579                 xx = block->line[line];
00580                 yy += hh;
00581                 hh = fsize + 2;
00582               }
00583               else if (*ptr == '\t')
00584               {
00585                 // Do tabs every 8 columns...
00586                 while (((s - buf) & 7))
00587                   *s++ = ' ';
00588               }
00589               else
00590                 *s++ = ' ';
00591 
00592               if ((fsize + 2) > hh)
00593                 hh = fsize + 2;
00594 
00595               ptr ++;
00596             }
00597 
00598             if (s > buf)
00599             {
00600               *s = '\0';
00601               s = buf;
00602 
00603               hv_draw(buf, xx + x() - leftline_, yy + y());
00604               ww = (int)fl_width(buf);
00605               if (underline) fl_xyline(xx + x() - leftline_, yy + y() + 1,
00606                                        xx + x() - leftline_ + ww);
00607               xx += ww;
00608               current_pos = ptr-value_;
00609             }
00610 
00611             needspace = 0;
00612           }
00613           else
00614           {
00615             s = buf;
00616 
00617             while (isspace((*ptr)&255))
00618               ptr ++;
00619             current_pos = ptr-value_;
00620           }
00621         }
00622 
00623         if (*ptr == '<')
00624         {
00625           ptr ++;
00626 
00627           if (strncmp(ptr, "!--", 3) == 0)
00628           {
00629             // Comment...
00630             ptr += 3;
00631             if ((ptr = strstr(ptr, "-->")) != NULL)
00632             {
00633               ptr += 3;
00634               continue;
00635             }
00636             else
00637               break;
00638           }
00639 
00640           while (*ptr && *ptr != '>' && !isspace((*ptr)&255))
00641             if (s < (buf + sizeof(buf) - 1))
00642               *s++ = *ptr++;
00643             else
00644               ptr ++;
00645 
00646           *s = '\0';
00647           s = buf;
00648 
00649           attrs = ptr;
00650           while (*ptr && *ptr != '>')
00651             ptr ++;
00652 
00653           if (*ptr == '>')
00654             ptr ++;
00655 
00656           // end of command reached, set the supposed start of printed eord here
00657           current_pos = ptr-value_;
00658           if (strcasecmp(buf, "HEAD") == 0)
00659             head = 1;
00660           else if (strcasecmp(buf, "BR") == 0)
00661           {
00662             if (line < 31)
00663               line ++;
00664             xx = block->line[line];
00665             yy += hh;
00666             hh = 0;
00667           }
00668           else if (strcasecmp(buf, "HR") == 0)
00669           {
00670             fl_line(block->x + x(), yy + y(), block->w + x(),
00671                     yy + y());
00672 
00673             if (line < 31)
00674               line ++;
00675             xx = block->line[line];
00676             yy += 2 * hh;
00677             hh = 0;
00678           }
00679           else if (strcasecmp(buf, "CENTER") == 0 ||
00680                    strcasecmp(buf, "P") == 0 ||
00681                    strcasecmp(buf, "H1") == 0 ||
00682                    strcasecmp(buf, "H2") == 0 ||
00683                    strcasecmp(buf, "H3") == 0 ||
00684                    strcasecmp(buf, "H4") == 0 ||
00685                    strcasecmp(buf, "H5") == 0 ||
00686                    strcasecmp(buf, "H6") == 0 ||
00687                    strcasecmp(buf, "UL") == 0 ||
00688                    strcasecmp(buf, "OL") == 0 ||
00689                    strcasecmp(buf, "DL") == 0 ||
00690                    strcasecmp(buf, "LI") == 0 ||
00691                    strcasecmp(buf, "DD") == 0 ||
00692                    strcasecmp(buf, "DT") == 0 ||
00693                    strcasecmp(buf, "PRE") == 0)
00694           {
00695             if (tolower(buf[0]) == 'h')
00696             {
00697               font  = FL_HELVETICA_BOLD;
00698               fsize = textsize_ + '7' - buf[1];
00699             }
00700             else if (strcasecmp(buf, "DT") == 0)
00701             {
00702               font  = textfont_ | FL_ITALIC;
00703               fsize = textsize_;
00704             }
00705             else if (strcasecmp(buf, "PRE") == 0)
00706             {
00707               font  = FL_COURIER;
00708               fsize = textsize_;
00709               pre   = 1;
00710             }
00711 
00712             if (strcasecmp(buf, "LI") == 0)
00713             {
00714 //            fl_font(FL_SYMBOL, fsize); // The default SYMBOL font on my XP box is not Unicode...
00715               char buf[8];
00716               wchar_t b[] = {0x2022, 0x0};
00717 //            buf[fl_unicode2utf(b, 1, buf)] = 0;
00718               unsigned dstlen = fl_utf8fromwc(buf, 8, b, 1);
00719               buf[dstlen] = 0;
00720               hv_draw(buf, xx - fsize + x() - leftline_, yy + y());
00721             }
00722 
00723             pushfont(font, fsize);
00724           }
00725           else if (strcasecmp(buf, "A") == 0 &&
00726                    get_attr(attrs, "HREF", attr, sizeof(attr)) != NULL)
00727           {
00728             fl_color(linkcolor_);
00729             underline = 1;
00730           }
00731           else if (strcasecmp(buf, "/A") == 0)
00732           {
00733             fl_color(textcolor_);
00734             underline = 0;
00735           }
00736           else if (strcasecmp(buf, "FONT") == 0)
00737           {
00738             if (get_attr(attrs, "COLOR", attr, sizeof(attr)) != NULL) {
00739               textcolor_ = get_color(attr, textcolor_);
00740             }
00741 
00742             if (get_attr(attrs, "FACE", attr, sizeof(attr)) != NULL) {
00743               if (!strncasecmp(attr, "helvetica", 9) ||
00744                   !strncasecmp(attr, "arial", 5) ||
00745                   !strncasecmp(attr, "sans", 4)) font = FL_HELVETICA;
00746               else if (!strncasecmp(attr, "times", 5) ||
00747                        !strncasecmp(attr, "serif", 5)) font = FL_TIMES;
00748               else if (!strncasecmp(attr, "symbol", 6)) font = FL_SYMBOL;
00749               else font = FL_COURIER;
00750             }
00751 
00752             if (get_attr(attrs, "SIZE", attr, sizeof(attr)) != NULL) {
00753               if (isdigit(attr[0] & 255)) {
00754                 // Absolute size
00755                 fsize = (int)(textsize_ * pow(1.2, atof(attr) - 3.0));
00756               } else {
00757                 // Relative size
00758                 fsize = (int)(fsize * pow(1.2, atof(attr) - 3.0));
00759               }
00760             }
00761 
00762             pushfont(font, fsize);
00763           }
00764           else if (strcasecmp(buf, "/FONT") == 0)
00765           {
00766             popfont(font, fsize, textcolor_);
00767           }
00768           else if (strcasecmp(buf, "U") == 0)
00769             underline = 1;
00770           else if (strcasecmp(buf, "/U") == 0)
00771             underline = 0;
00772           else if (strcasecmp(buf, "B") == 0 ||
00773                    strcasecmp(buf, "STRONG") == 0)
00774             pushfont(font |= FL_BOLD, fsize);
00775           else if (strcasecmp(buf, "TD") == 0 ||
00776                    strcasecmp(buf, "TH") == 0)
00777           {
00778             int tx, ty, tw, th;
00779 
00780             if (tolower(buf[1]) == 'h')
00781               pushfont(font |= FL_BOLD, fsize);
00782             else
00783               pushfont(font = textfont_, fsize);
00784 
00785             tx = block->x - 4 - leftline_;
00786             ty = block->y - topline_ - fsize - 3;
00787             tw = block->w - block->x + 7;
00788             th = block->h + fsize - 5;
00789 
00790             if (tx < 0)
00791             {
00792               tw += tx;
00793               tx  = 0;
00794             }
00795 
00796             if (ty < 0)
00797             {
00798               th += ty;
00799               ty  = 0;
00800             }
00801 
00802             tx += x();
00803             ty += y();
00804 
00805             if (block->bgcolor != bgcolor_)
00806             {
00807               fl_color(block->bgcolor);
00808               fl_rectf(tx, ty, tw, th);
00809               fl_color(textcolor_);
00810             }
00811 
00812             if (block->border)
00813               fl_rect(tx, ty, tw, th);
00814           }
00815           else if (strcasecmp(buf, "I") == 0 ||
00816                    strcasecmp(buf, "EM") == 0)
00817             pushfont(font |= FL_ITALIC, fsize);
00818           else if (strcasecmp(buf, "CODE") == 0 ||
00819                    strcasecmp(buf, "TT") == 0)
00820             pushfont(font = FL_COURIER, fsize);
00821           else if (strcasecmp(buf, "KBD") == 0)
00822             pushfont(font = FL_COURIER_BOLD, fsize);
00823           else if (strcasecmp(buf, "VAR") == 0)
00824             pushfont(font = FL_COURIER_ITALIC, fsize);
00825           else if (strcasecmp(buf, "/HEAD") == 0)
00826             head = 0;
00827           else if (strcasecmp(buf, "/H1") == 0 ||
00828                    strcasecmp(buf, "/H2") == 0 ||
00829                    strcasecmp(buf, "/H3") == 0 ||
00830                    strcasecmp(buf, "/H4") == 0 ||
00831                    strcasecmp(buf, "/H5") == 0 ||
00832                    strcasecmp(buf, "/H6") == 0 ||
00833                    strcasecmp(buf, "/B") == 0 ||
00834                    strcasecmp(buf, "/STRONG") == 0 ||
00835                    strcasecmp(buf, "/I") == 0 ||
00836                    strcasecmp(buf, "/EM") == 0 ||
00837                    strcasecmp(buf, "/CODE") == 0 ||
00838                    strcasecmp(buf, "/TT") == 0 ||
00839                    strcasecmp(buf, "/KBD") == 0 ||
00840                    strcasecmp(buf, "/VAR") == 0)
00841             popfont(font, fsize, fcolor);
00842           else if (strcasecmp(buf, "/PRE") == 0)
00843           {
00844             popfont(font, fsize, fcolor);
00845             pre = 0;
00846           }
00847           else if (strcasecmp(buf, "IMG") == 0)
00848           {
00849             Fl_Shared_Image *img = 0;
00850             int         width, height;
00851             char        wattr[8], hattr[8];
00852 
00853 
00854             get_attr(attrs, "WIDTH", wattr, sizeof(wattr));
00855             get_attr(attrs, "HEIGHT", hattr, sizeof(hattr));
00856             width  = get_length(wattr);
00857             height = get_length(hattr);
00858 
00859             if (get_attr(attrs, "SRC", attr, sizeof(attr))) {
00860               img = get_image(attr, width, height);
00861               if (!width) width = img->w();
00862               if (!height) height = img->h();
00863             }
00864 
00865             if (!width || !height) {
00866               if (get_attr(attrs, "ALT", attr, sizeof(attr)) == NULL) {
00867                 strcpy(attr, "IMG");
00868               }
00869             }
00870 
00871             ww = width;
00872 
00873             if (needspace && xx > block->x)
00874               xx += (int)fl_width(' ');
00875 
00876             if ((xx + ww) > block->w)
00877             {
00878               if (line < 31)
00879                 line ++;
00880 
00881               xx = block->line[line];
00882               yy += hh;
00883               hh = 0;
00884             }
00885 
00886             if (img) {
00887               img->draw(xx + x() - leftline_,
00888                         yy + y() - fl_height() + fl_descent() + 2);
00889             }
00890 
00891             xx += ww;
00892             if ((height + 2) > hh)
00893               hh = height + 2;
00894 
00895             needspace = 0;
00896           }
00897         }
00898         else if (*ptr == '\n' && pre)
00899         {
00900           *s = '\0';
00901           s = buf;
00902 
00903           hv_draw(buf, xx + x() - leftline_, yy + y());
00904 
00905           if (line < 31)
00906             line ++;
00907           xx = block->line[line];
00908           yy += hh;
00909           hh = fsize + 2;
00910           needspace = 0;
00911 
00912           ptr ++;
00913           current_pos = ptr-value_;
00914         }
00915         else if (isspace((*ptr)&255))
00916         {
00917           if (pre)
00918           {
00919             if (*ptr == ' ')
00920               *s++ = ' ';
00921             else
00922             {
00923               // Do tabs every 8 columns...
00924               while (((s - buf) & 7))
00925                 *s++ = ' ';
00926             }
00927           }
00928 
00929           ptr ++;
00930           if (!pre) current_pos = ptr-value_;
00931           needspace = 1;
00932         }
00933         else if (*ptr == '&')
00934         {
00935           ptr ++;
00936 
00937           int qch = quote_char(ptr);
00938 
00939           if (qch < 0)
00940             *s++ = '&';
00941           else {
00942             int l;
00943             l = fl_utf8encode((unsigned int) qch, s);
00944             if (l < 1) l = 1;
00945             s += l;
00946             ptr = strchr(ptr, ';') + 1;
00947           }
00948 
00949           if ((fsize + 2) > hh)
00950             hh = fsize + 2;
00951         }
00952         else
00953         {
00954           *s++ = *ptr++;
00955 
00956           if ((fsize + 2) > hh)
00957             hh = fsize + 2;
00958         }
00959       }
00960 
00961       *s = '\0';
00962 
00963       if (s > buf && !pre && !head)
00964       {
00965         ww = (int)fl_width(buf);
00966 
00967         if (needspace && xx > block->x)
00968           xx += (int)fl_width(' ');
00969 
00970         if ((xx + ww) > block->w)
00971         {
00972           if (line < 31)
00973             line ++;
00974           xx = block->line[line];
00975           yy += hh;
00976           hh = 0;
00977         }
00978       }
00979 
00980       if (s > buf && !head)
00981       {
00982         hv_draw(buf, xx + x() - leftline_, yy + y());
00983         if (underline) fl_xyline(xx + x() - leftline_, yy + y() + 1,
00984                                  xx + x() - leftline_ + ww);
00985         current_pos = ptr-value_;
00986       }
00987     }
00988 
00989   fl_pop_clip();
00990 }
00991 
00992 
00993 
00998 int                                             // O - Matching position or -1 if not found
00999 Fl_Help_View::find(const char *s,               // I - String to find
01000                    int        p)                // I - Starting position
01001 {
01002   int           i,                              // Looping var
01003                 c;                              // Current character
01004   Fl_Help_Block *b;                             // Current block
01005   const char    *bp,                            // Block matching pointer
01006                 *bs,                            // Start of current comparison
01007                 *sp;                            // Search string pointer
01008 
01009 
01010   // Range check input and value...
01011   if (!s || !value_) return -1;
01012 
01013   if (p < 0 || p >= (int)strlen(value_)) p = 0;
01014   else if (p > 0) p ++;
01015 
01016   // Look for the string...
01017   for (i = nblocks_, b = blocks_; i > 0; i --, b ++) {
01018     if (b->end < (value_ + p))
01019       continue;
01020 
01021     if (b->start < (value_ + p)) bp = value_ + p;
01022     else bp = b->start;
01023 
01024     for (sp = s, bs = bp; *sp && *bp && bp < b->end; bp ++) {
01025       if (*bp == '<') {
01026         // skip to end of element...
01027         while (*bp && bp < b->end && *bp != '>') bp ++;
01028         continue;
01029       } else if (*bp == '&') {
01030         // decode HTML entity...
01031         if ((c = quote_char(bp + 1)) < 0) c = '&';
01032         else bp = strchr(bp + 1, ';') + 1;
01033       } else c = *bp;
01034 
01035       if (tolower(*sp) == tolower(c)) sp ++;
01036       else {
01037         // No match, so reset to start of search...
01038         sp = s;
01039         bs ++;
01040         bp = bs;
01041       }
01042     }
01043 
01044     if (!*sp) {
01045       // Found a match!
01046       topline(b->y - b->h);
01047       return (b->end - value_);
01048     }
01049   }
01050 
01051   // No match!
01052   return (-1);
01053 }
01054 
01056 void Fl_Help_View::format() {
01057   int           i;              // Looping var
01058   int           done;           // Are we done yet?
01059   Fl_Help_Block *block,         // Current block
01060                 *cell;          // Current table cell
01061   int           cells[MAX_COLUMNS],
01062                                 // Cells in the current row...
01063                 row;            // Current table row (block number)
01064   const char    *ptr,           // Pointer into block
01065                 *start,         // Pointer to start of element
01066                 *attrs;         // Pointer to start of element attributes
01067   char          *s,             // Pointer into buffer
01068                 buf[1024],      // Text buffer
01069                 attr[1024],     // Attribute buffer
01070                 wattr[1024],    // Width attribute buffer
01071                 hattr[1024],    // Height attribute buffer
01072                 linkdest[1024]; // Link destination
01073   int           xx, yy, ww, hh; // Size of current text fragment
01074   int           line;           // Current line in block
01075   int           links;          // Links for current line
01076   Fl_Font       font;
01077   Fl_Fontsize   fsize;          // Current font and size
01078   Fl_Color      fcolor;         // Current font color
01079   unsigned char border;         // Draw border?
01080   int           talign,         // Current alignment
01081                 newalign,       // New alignment
01082                 head,           // In the <HEAD> section?
01083                 pre,            // <PRE> text?
01084                 needspace;      // Do we need whitespace?
01085   int           table_width,    // Width of table
01086                 table_offset;   // Offset of table
01087   int           column,         // Current table column number
01088                 columns[MAX_COLUMNS];
01089                                 // Column widths
01090   Fl_Color      tc, rc;         // Table/row background color
01091   Fl_Boxtype    b = box() ? box() : FL_DOWN_BOX;
01092                                 // Box to draw...
01093   fl_margins    margins;        // Left margin stack...
01094 
01095 
01096   // Reset document width...
01097   int scrollsize = scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size();
01098   hsize_ = w() - scrollsize - Fl::box_dw(b);
01099 
01100   done = 0;
01101   while (!done)
01102   {
01103     // Reset state variables...
01104     done       = 1;
01105     nblocks_   = 0;
01106     nlinks_    = 0;
01107     ntargets_  = 0;
01108     size_      = 0;
01109     bgcolor_   = color();
01110     textcolor_ = textcolor();
01111     linkcolor_ = fl_contrast(FL_BLUE, color());
01112 
01113     tc = rc = bgcolor_;
01114 
01115     strcpy(title_, "Untitled");
01116 
01117     if (!value_)
01118       return;
01119 
01120     // Setup for formatting...
01121     initfont(font, fsize, fcolor);
01122 
01123     line         = 0;
01124     links        = 0;
01125     xx           = margins.clear();
01126     yy           = fsize + 2;
01127     ww           = 0;
01128     column       = 0;
01129     border       = 0;
01130     hh           = 0;
01131     block        = add_block(value_, xx, yy, hsize_, 0);
01132     row          = 0;
01133     head         = 0;
01134     pre          = 0;
01135     talign       = LEFT;
01136     newalign     = LEFT;
01137     needspace    = 0;
01138     linkdest[0]  = '\0';
01139     table_offset = 0;
01140 
01141     // Html text character loop
01142     for (ptr = value_, s = buf; *ptr;)
01143     {
01144       // End of word?
01145       if ((*ptr == '<' || isspace((*ptr)&255)) && s > buf)
01146       {
01147         // Get width of word parsed so far...
01148         *s = '\0';
01149         ww = (int)fl_width(buf);
01150 
01151         if (!head && !pre)
01152         {
01153           // Check width...
01154           if (ww > hsize_) {
01155             hsize_ = ww;
01156             done   = 0;
01157             break;
01158           }
01159 
01160           if (needspace && xx > block->x)
01161             ww += (int)fl_width(' ');
01162 
01163   //        printf("line = %d, xx = %d, ww = %d, block->x = %d, block->w = %d\n",
01164   //           line, xx, ww, block->x, block->w);
01165 
01166           if ((xx + ww) > block->w)
01167           {
01168             line     = do_align(block, line, xx, newalign, links);
01169             xx       = block->x;
01170             yy       += hh;
01171             block->h += hh;
01172             hh       = 0;
01173           }
01174 
01175           if (linkdest[0])
01176             add_link(linkdest, xx, yy - fsize, ww, fsize);
01177 
01178           xx += ww;
01179           if ((fsize + 2) > hh)
01180             hh = fsize + 2;
01181 
01182           needspace = 0;
01183         }
01184         else if (pre)
01185         {
01186           // Add a link as needed...
01187           if (linkdest[0])
01188             add_link(linkdest, xx, yy - hh, ww, hh);
01189 
01190           xx += ww;
01191           if ((fsize + 2) > hh)
01192             hh = fsize + 2;
01193 
01194           // Handle preformatted text...
01195           while (isspace((*ptr)&255))
01196           {
01197             if (*ptr == '\n')
01198             {
01199               if (xx > hsize_) break;
01200 
01201               line     = do_align(block, line, xx, newalign, links);
01202               xx       = block->x;
01203               yy       += hh;
01204               block->h += hh;
01205               hh       = fsize + 2;
01206             }
01207             else
01208               xx += (int)fl_width(' ');
01209 
01210             if ((fsize + 2) > hh)
01211               hh = fsize + 2;
01212 
01213             ptr ++;
01214           }
01215 
01216           if (xx > hsize_) {
01217             hsize_ = xx;
01218             done   = 0;
01219             break;
01220           }
01221 
01222           needspace = 0;
01223         }
01224         else
01225         {
01226           // Handle normal text or stuff in the <HEAD> section...
01227           while (isspace((*ptr)&255))
01228             ptr ++;
01229         }
01230 
01231         s = buf;
01232       }
01233 
01234       if (*ptr == '<')
01235       {
01236         // Handle html tags..
01237         start = ptr;
01238         ptr ++;
01239 
01240         if (strncmp(ptr, "!--", 3) == 0)
01241         {
01242           // Comment...
01243           ptr += 3;
01244           if ((ptr = strstr(ptr, "-->")) != NULL)
01245           {
01246             ptr += 3;
01247             continue;
01248           }
01249           else
01250             break;
01251         }
01252 
01253         while (*ptr && *ptr != '>' && !isspace((*ptr)&255))
01254           if (s < (buf + sizeof(buf) - 1))
01255             *s++ = *ptr++;
01256           else
01257             ptr ++;
01258 
01259         *s = '\0';
01260         s = buf;
01261 
01262 //        puts(buf);
01263 
01264         attrs = ptr;
01265         while (*ptr && *ptr != '>')
01266           ptr ++;
01267 
01268         if (*ptr == '>')
01269           ptr ++;
01270 
01271         if (strcasecmp(buf, "HEAD") == 0)
01272           head = 1;
01273         else if (strcasecmp(buf, "/HEAD") == 0)
01274           head = 0;
01275         else if (strcasecmp(buf, "TITLE") == 0)
01276         {
01277           // Copy the title in the document...
01278           for (s = title_;
01279                *ptr != '<' && *ptr && s < (title_ + sizeof(title_) - 1);
01280                *s++ = *ptr++);
01281 
01282           *s = '\0';
01283           s = buf;
01284         }
01285         else if (strcasecmp(buf, "A") == 0)
01286         {
01287           if (get_attr(attrs, "NAME", attr, sizeof(attr)) != NULL)
01288             add_target(attr, yy - fsize - 2);
01289 
01290           if (get_attr(attrs, "HREF", attr, sizeof(attr)) != NULL)
01291             strlcpy(linkdest, attr, sizeof(linkdest));
01292         }
01293         else if (strcasecmp(buf, "/A") == 0)
01294           linkdest[0] = '\0';
01295         else if (strcasecmp(buf, "BODY") == 0)
01296         {
01297           bgcolor_   = get_color(get_attr(attrs, "BGCOLOR", attr, sizeof(attr)),
01298                                  color());
01299           textcolor_ = get_color(get_attr(attrs, "TEXT", attr, sizeof(attr)),
01300                                  textcolor());
01301           linkcolor_ = get_color(get_attr(attrs, "LINK", attr, sizeof(attr)),
01302                                  fl_contrast(FL_BLUE, color()));
01303         }
01304         else if (strcasecmp(buf, "BR") == 0)
01305         {
01306           line     = do_align(block, line, xx, newalign, links);
01307           xx       = block->x;
01308           block->h += hh;
01309           yy       += hh;
01310           hh       = 0;
01311         }
01312         else if (strcasecmp(buf, "CENTER") == 0 ||
01313                  strcasecmp(buf, "P") == 0 ||
01314                  strcasecmp(buf, "H1") == 0 ||
01315                  strcasecmp(buf, "H2") == 0 ||
01316                  strcasecmp(buf, "H3") == 0 ||
01317                  strcasecmp(buf, "H4") == 0 ||
01318                  strcasecmp(buf, "H5") == 0 ||
01319                  strcasecmp(buf, "H6") == 0 ||
01320                  strcasecmp(buf, "UL") == 0 ||
01321                  strcasecmp(buf, "OL") == 0 ||
01322                  strcasecmp(buf, "DL") == 0 ||
01323                  strcasecmp(buf, "LI") == 0 ||
01324                  strcasecmp(buf, "DD") == 0 ||
01325                  strcasecmp(buf, "DT") == 0 ||
01326                  strcasecmp(buf, "HR") == 0 ||
01327                  strcasecmp(buf, "PRE") == 0 ||
01328                  strcasecmp(buf, "TABLE") == 0)
01329         {
01330           block->end = start;
01331           line       = do_align(block, line, xx, newalign, links);
01332           newalign   = strcasecmp(buf, "CENTER") ? LEFT : CENTER;
01333           xx         = block->x;
01334           block->h   += hh;
01335 
01336           if (strcasecmp(buf, "UL") == 0 ||
01337               strcasecmp(buf, "OL") == 0 ||
01338               strcasecmp(buf, "DL") == 0)
01339           {
01340             block->h += fsize + 2;
01341             xx       = margins.push(4 * fsize);
01342           }
01343           else if (strcasecmp(buf, "TABLE") == 0)
01344           {
01345             if (get_attr(attrs, "BORDER", attr, sizeof(attr)))
01346               border = (uchar)atoi(attr);
01347             else
01348               border = 0;
01349 
01350             tc = rc = get_color(get_attr(attrs, "BGCOLOR", attr, sizeof(attr)), bgcolor_);
01351 
01352             block->h += fsize + 2;
01353 
01354             format_table(&table_width, columns, start);
01355 
01356             if ((xx + table_width) > hsize_) {
01357 #ifdef DEBUG
01358               printf("xx=%d, table_width=%d, hsize_=%d\n", xx, table_width,
01359                      hsize_);
01360 #endif // DEBUG
01361               hsize_ = xx + table_width;
01362               done   = 0;
01363               break;
01364             }
01365 
01366             switch (get_align(attrs, talign))
01367             {
01368               default :
01369                   table_offset = 0;
01370                   break;
01371 
01372               case CENTER :
01373                   table_offset = (hsize_ - table_width) / 2 - textsize_;
01374                   break;
01375 
01376               case RIGHT :
01377                   table_offset = hsize_ - table_width - textsize_;
01378                   break;
01379             }
01380 
01381             column = 0;
01382           }
01383 
01384           if (tolower(buf[0]) == 'h' && isdigit(buf[1]))
01385           {
01386             font  = FL_HELVETICA_BOLD;
01387             fsize = textsize_ + '7' - buf[1];
01388           }
01389           else if (strcasecmp(buf, "DT") == 0)
01390           {
01391             font  = textfont_ | FL_ITALIC;
01392             fsize = textsize_;
01393           }
01394           else if (strcasecmp(buf, "PRE") == 0)
01395           {
01396             font  = FL_COURIER;
01397             fsize = textsize_;
01398             pre   = 1;
01399           }
01400           else
01401           {
01402             font  = textfont_;
01403             fsize = textsize_;
01404           }
01405 
01406           pushfont(font, fsize);
01407 
01408           yy = block->y + block->h;
01409           hh = 0;
01410 
01411           if ((tolower(buf[0]) == 'h' && isdigit(buf[1])) ||
01412               strcasecmp(buf, "DD") == 0 ||
01413               strcasecmp(buf, "DT") == 0 ||
01414               strcasecmp(buf, "P") == 0)
01415             yy += fsize + 2;
01416           else if (strcasecmp(buf, "HR") == 0)
01417           {
01418             hh += 2 * fsize;
01419             yy += fsize;
01420           }
01421 
01422           if (row)
01423             block = add_block(start, xx, yy, block->w, 0);
01424           else
01425             block = add_block(start, xx, yy, hsize_, 0);
01426 
01427           needspace = 0;
01428           line      = 0;
01429 
01430           if (strcasecmp(buf, "CENTER") == 0)
01431             newalign = talign = CENTER;
01432           else
01433             newalign = get_align(attrs, talign);
01434         }
01435         else if (strcasecmp(buf, "/CENTER") == 0 ||
01436                  strcasecmp(buf, "/P") == 0 ||
01437                  strcasecmp(buf, "/H1") == 0 ||
01438                  strcasecmp(buf, "/H2") == 0 ||
01439                  strcasecmp(buf, "/H3") == 0 ||
01440                  strcasecmp(buf, "/H4") == 0 ||
01441                  strcasecmp(buf, "/H5") == 0 ||
01442                  strcasecmp(buf, "/H6") == 0 ||
01443                  strcasecmp(buf, "/PRE") == 0 ||
01444                  strcasecmp(buf, "/UL") == 0 ||
01445                  strcasecmp(buf, "/OL") == 0 ||
01446                  strcasecmp(buf, "/DL") == 0 ||
01447                  strcasecmp(buf, "/TABLE") == 0)
01448         {
01449           line       = do_align(block, line, xx, newalign, links);
01450           xx         = block->x;
01451           block->end = ptr;
01452 
01453           if (strcasecmp(buf, "/UL") == 0 ||
01454               strcasecmp(buf, "/OL") == 0 ||
01455               strcasecmp(buf, "/DL") == 0)
01456           {
01457             xx       = margins.pop();
01458             block->h += fsize + 2;
01459           }
01460           else if (strcasecmp(buf, "/TABLE") == 0) 
01461           {
01462             block->h += fsize + 2;
01463             xx       = margins.current();
01464           }
01465           else if (strcasecmp(buf, "/PRE") == 0)
01466           {
01467             pre = 0;
01468             hh  = 0;
01469           }
01470           else if (strcasecmp(buf, "/CENTER") == 0)
01471             talign = LEFT;
01472 
01473           popfont(font, fsize, fcolor);
01474 
01475           //#if defined(__GNUC__)
01476           //#warning FIXME this isspace & 255 test will probably not work on a utf8 stream... And we use it everywhere!
01477           //#endif /*__GNUC__*/
01478           while (isspace((*ptr)&255))
01479             ptr ++;
01480 
01481           block->h += hh;
01482           yy       += hh;
01483 
01484           if (tolower(buf[2]) == 'l')
01485             yy += fsize + 2;
01486 
01487           if (row)
01488             block = add_block(ptr, xx, yy, block->w, 0);
01489           else
01490             block = add_block(ptr, xx, yy, hsize_, 0);
01491 
01492           needspace = 0;
01493           hh        = 0;
01494           line      = 0;
01495           newalign  = talign;
01496         }
01497         else if (strcasecmp(buf, "TR") == 0)
01498         {
01499           block->end = start;
01500           line       = do_align(block, line, xx, newalign, links);
01501           xx         = block->x;
01502           block->h   += hh;
01503 
01504           if (row)
01505           {
01506             yy = blocks_[row].y + blocks_[row].h;
01507 
01508             for (cell = blocks_ + row + 1; cell <= block; cell ++)
01509               if ((cell->y + cell->h) > yy)
01510                 yy = cell->y + cell->h;
01511 
01512             block = blocks_ + row;
01513 
01514             block->h = yy - block->y + 2;
01515 
01516             for (i = 0; i < column; i ++)
01517               if (cells[i])
01518               {
01519                 cell = blocks_ + cells[i];
01520                 cell->h = block->h;
01521               }
01522           }
01523 
01524           memset(cells, 0, sizeof(cells));
01525 
01526           yy        = block->y + block->h - 4;
01527           hh        = 0;
01528           block     = add_block(start, xx, yy, hsize_, 0);
01529           row       = block - blocks_;
01530           needspace = 0;
01531           column    = 0;
01532           line      = 0;
01533 
01534           rc = get_color(get_attr(attrs, "BGCOLOR", attr, sizeof(attr)), tc);
01535         }
01536         else if (strcasecmp(buf, "/TR") == 0 && row)
01537         {
01538           line       = do_align(block, line, xx, newalign, links);
01539           block->end = start;
01540           block->h   += hh;
01541           talign     = LEFT;
01542 
01543           xx = blocks_[row].x;
01544           yy = blocks_[row].y + blocks_[row].h;
01545 
01546           for (cell = blocks_ + row + 1; cell <= block; cell ++)
01547             if ((cell->y + cell->h) > yy)
01548               yy = cell->y + cell->h;
01549 
01550           block = blocks_ + row;
01551 
01552           block->h = yy - block->y + 2;
01553 
01554           for (i = 0; i < column; i ++)
01555             if (cells[i])
01556             {
01557               cell = blocks_ + cells[i];
01558               cell->h = block->h;
01559             }
01560 
01561           yy        = block->y + block->h /*- 4*/;
01562           block     = add_block(start, xx, yy, hsize_, 0);
01563           needspace = 0;
01564           row       = 0;
01565           line      = 0;
01566         }
01567         else if ((strcasecmp(buf, "TD") == 0 ||
01568                   strcasecmp(buf, "TH") == 0) && row)
01569         {
01570           int   colspan;                // COLSPAN attribute
01571 
01572 
01573           line       = do_align(block, line, xx, newalign, links);
01574           block->end = start;
01575           block->h   += hh;
01576 
01577           if (strcasecmp(buf, "TH") == 0)
01578             font = textfont_ | FL_BOLD;
01579           else
01580             font = textfont_;
01581 
01582           fsize = textsize_;
01583 
01584           xx = blocks_[row].x + fsize + 3 + table_offset;
01585           for (i = 0; i < column; i ++)
01586             xx += columns[i] + 6;
01587 
01588           margins.push(xx - margins.current());
01589 
01590           if (get_attr(attrs, "COLSPAN", attr, sizeof(attr)) != NULL)
01591             colspan = atoi(attr);
01592           else
01593             colspan = 1;
01594 
01595           for (i = 0, ww = -6; i < colspan; i ++)
01596             ww += columns[column + i] + 6;
01597 
01598           if (block->end == block->start && nblocks_ > 1)
01599           {
01600             nblocks_ --;
01601             block --;
01602           }
01603 
01604           pushfont(font, fsize);
01605 
01606           yy        = blocks_[row].y;
01607           hh        = 0;
01608           block     = add_block(start, xx, yy, xx + ww, 0, border);
01609           needspace = 0;
01610           line      = 0;
01611           newalign  = get_align(attrs, tolower(buf[1]) == 'h' ? CENTER : LEFT);
01612           talign    = newalign;
01613 
01614           cells[column] = block - blocks_;
01615 
01616           column += colspan;
01617 
01618           block->bgcolor = get_color(get_attr(attrs, "BGCOLOR", attr,
01619                                               sizeof(attr)), rc);
01620         }
01621         else if ((strcasecmp(buf, "/TD") == 0 ||
01622                   strcasecmp(buf, "/TH") == 0) && row)
01623         {
01624           line = do_align(block, line, xx, newalign, links);
01625           popfont(font, fsize, fcolor);
01626           xx = margins.pop();
01627           talign = LEFT;
01628         }
01629         else if (strcasecmp(buf, "FONT") == 0)
01630         {
01631           if (get_attr(attrs, "FACE", attr, sizeof(attr)) != NULL) {
01632             if (!strncasecmp(attr, "helvetica", 9) ||
01633                 !strncasecmp(attr, "arial", 5) ||
01634                 !strncasecmp(attr, "sans", 4)) font = FL_HELVETICA;
01635             else if (!strncasecmp(attr, "times", 5) ||
01636                      !strncasecmp(attr, "serif", 5)) font = FL_TIMES;
01637             else if (!strncasecmp(attr, "symbol", 6)) font = FL_SYMBOL;
01638             else font = FL_COURIER;
01639           }
01640 
01641           if (get_attr(attrs, "SIZE", attr, sizeof(attr)) != NULL) {
01642             if (isdigit(attr[0] & 255)) {
01643               // Absolute size
01644               fsize = (int)(textsize_ * pow(1.2, atoi(attr) - 3.0));
01645             } else {
01646               // Relative size
01647               fsize = (int)(fsize * pow(1.2, atoi(attr)));
01648             }
01649           }
01650 
01651           pushfont(font, fsize);
01652         }
01653         else if (strcasecmp(buf, "/FONT") == 0)
01654           popfont(font, fsize, fcolor);
01655         else if (strcasecmp(buf, "B") == 0 ||
01656                  strcasecmp(buf, "STRONG") == 0)
01657           pushfont(font |= FL_BOLD, fsize);
01658         else if (strcasecmp(buf, "I") == 0 ||
01659                  strcasecmp(buf, "EM") == 0)
01660           pushfont(font |= FL_ITALIC, fsize);
01661         else if (strcasecmp(buf, "CODE") == 0 ||
01662                  strcasecmp(buf, "TT") == 0)
01663           pushfont(font = FL_COURIER, fsize);
01664         else if (strcasecmp(buf, "KBD") == 0)
01665           pushfont(font = FL_COURIER_BOLD, fsize);
01666         else if (strcasecmp(buf, "VAR") == 0)
01667           pushfont(font = FL_COURIER_ITALIC, fsize);
01668         else if (strcasecmp(buf, "/B") == 0 ||
01669                  strcasecmp(buf, "/STRONG") == 0 ||
01670                  strcasecmp(buf, "/I") == 0 ||
01671                  strcasecmp(buf, "/EM") == 0 ||
01672                  strcasecmp(buf, "/CODE") == 0 ||
01673                  strcasecmp(buf, "/TT") == 0 ||
01674                  strcasecmp(buf, "/KBD") == 0 ||
01675                  strcasecmp(buf, "/VAR") == 0)
01676           popfont(font, fsize, fcolor);
01677         else if (strcasecmp(buf, "IMG") == 0)
01678         {
01679           Fl_Shared_Image       *img = 0;
01680           int           width;
01681           int           height;
01682 
01683 
01684           get_attr(attrs, "WIDTH", wattr, sizeof(wattr));
01685           get_attr(attrs, "HEIGHT", hattr, sizeof(hattr));
01686           width  = get_length(wattr);
01687           height = get_length(hattr);
01688 
01689           if (get_attr(attrs, "SRC", attr, sizeof(attr))) {
01690             img    = get_image(attr, width, height);
01691             width  = img->w();
01692             height = img->h();
01693           }
01694 
01695           ww = width;
01696 
01697           if (ww > hsize_) {
01698             hsize_ = ww;
01699             done   = 0;
01700             break;
01701           }
01702 
01703           if (needspace && xx > block->x)
01704             ww += (int)fl_width(' ');
01705 
01706           if ((xx + ww) > block->w)
01707           {
01708             line     = do_align(block, line, xx, newalign, links);
01709             xx       = block->x;
01710             yy       += hh;
01711             block->h += hh;
01712             hh       = 0;
01713           }
01714 
01715           if (linkdest[0])
01716             add_link(linkdest, xx, yy - height, ww, height);
01717 
01718           xx += ww;
01719           if ((height + 2) > hh)
01720             hh = height + 2;
01721 
01722           needspace = 0;
01723         }
01724       }
01725       else if (*ptr == '\n' && pre)
01726       {
01727         if (linkdest[0])
01728           add_link(linkdest, xx, yy - hh, ww, hh);
01729 
01730         if (xx > hsize_) {
01731           hsize_ = xx;
01732           done   = 0;
01733           break;
01734         }
01735 
01736         line      = do_align(block, line, xx, newalign, links);
01737         xx        = block->x;
01738         yy        += hh;
01739         block->h  += hh;
01740         needspace = 0;
01741         ptr ++;
01742       }
01743       else if (isspace((*ptr)&255))
01744       {
01745         needspace = 1;
01746         if ( pre ) {
01747           xx += (int)fl_width(' ');
01748         }
01749         ptr ++;
01750       }
01751       else if (*ptr == '&' && s < (buf + sizeof(buf) - 1))
01752       {
01753         // Handle html '&' codes, eg. "&amp;"
01754         ptr ++;
01755 
01756         int qch = quote_char(ptr);
01757 
01758         if (qch < 0)
01759           *s++ = '&';
01760         else {
01761           int l;
01762           l = fl_utf8encode((unsigned int) qch, s);
01763           if (l < 1) l = 1;
01764           s += l;
01765           ptr = strchr(ptr, ';') + 1;
01766         }
01767 
01768         if ((fsize + 2) > hh)
01769           hh = fsize + 2;
01770       }
01771       else
01772       {
01773         if (s < (buf + sizeof(buf) - 1))
01774           *s++ = *ptr++;
01775         else
01776           ptr ++;
01777 
01778         if ((fsize + 2) > hh)
01779           hh = fsize + 2;
01780       }
01781     }
01782 
01783     if (s > buf && !head)
01784     {
01785       *s = '\0';
01786       ww = (int)fl_width(buf);
01787 
01788   //    printf("line = %d, xx = %d, ww = %d, block->x = %d, block->w = %d\n",
01789   //       line, xx, ww, block->x, block->w);
01790 
01791       if (ww > hsize_) {
01792         hsize_ = ww;
01793         done   = 0;
01794         break;
01795       }
01796 
01797       if (needspace && xx > block->x)
01798         ww += (int)fl_width(' ');
01799 
01800       if ((xx + ww) > block->w)
01801       {
01802         line     = do_align(block, line, xx, newalign, links);
01803         xx       = block->x;
01804         yy       += hh;
01805         block->h += hh;
01806         hh       = 0;
01807       }
01808 
01809       if (linkdest[0])
01810         add_link(linkdest, xx, yy - fsize, ww, fsize);
01811 
01812       xx += ww;
01813     }
01814 
01815     do_align(block, line, xx, newalign, links);
01816 
01817     block->end = ptr;
01818     size_      = yy + hh;
01819   }
01820 
01821 //  printf("margins.depth_=%d\n", margins.depth_);
01822 
01823   if (ntargets_ > 1)
01824     qsort(targets_, ntargets_, sizeof(Fl_Help_Target),
01825           (compare_func_t)compare_targets);
01826 
01827   int dx = Fl::box_dw(b) - Fl::box_dx(b);
01828   int dy = Fl::box_dh(b) - Fl::box_dy(b);
01829   int ss = scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size();
01830   int dw = Fl::box_dw(b) + ss;
01831   int dh = Fl::box_dh(b);
01832 
01833   if (hsize_ > (w() - dw)) {
01834     hscrollbar_.show();
01835 
01836     dh += ss;
01837 
01838     if (size_ < (h() - dh)) {
01839       scrollbar_.hide();
01840       hscrollbar_.resize(x() + Fl::box_dx(b), y() + h() - ss - dy,
01841                          w() - Fl::box_dw(b), ss);
01842     } else {
01843       scrollbar_.show();
01844       scrollbar_.resize(x() + w() - ss - dx, y() + Fl::box_dy(b),
01845                         ss, h() - ss - Fl::box_dh(b));
01846       hscrollbar_.resize(x() + Fl::box_dx(b), y() + h() - ss - dy,
01847                          w() - ss - Fl::box_dw(b), ss);
01848     }
01849   } else {
01850     hscrollbar_.hide();
01851 
01852     if (size_ < (h() - dh)) scrollbar_.hide();
01853     else {
01854       scrollbar_.resize(x() + w() - ss - dx, y() + Fl::box_dy(b),
01855                         ss, h() - Fl::box_dh(b));
01856       scrollbar_.show();
01857     }
01858   }
01859 
01860   // Reset scrolling if it needs to be...
01861   if (scrollbar_.visible()) {
01862     int temph = h() - Fl::box_dh(b);
01863     if (hscrollbar_.visible()) temph -= ss;
01864     if ((topline_ + temph) > size_) topline(size_ - temph);
01865     else topline(topline_);
01866   } else topline(0);
01867 
01868   if (hscrollbar_.visible()) {
01869     int tempw = w() - ss - Fl::box_dw(b);
01870     if ((leftline_ + tempw) > hsize_) leftline(hsize_ - tempw);
01871     else leftline(leftline_);
01872   } else leftline(0);
01873 }
01874 
01875 
01877 void
01878 Fl_Help_View::format_table(int        *table_width,     // O - Total table width
01879                            int        *columns,         // O - Column widths
01880                            const char *table)           // I - Pointer to start of table
01881 {
01882   int           column,                                 // Current column
01883                 num_columns,                            // Number of columns
01884                 colspan,                                // COLSPAN attribute
01885                 width,                                  // Current width
01886                 temp_width,                             // Temporary width
01887                 max_width,                              // Maximum width
01888                 incell,                                 // In a table cell?
01889                 pre,                                    // <PRE> text?
01890                 needspace;                              // Need whitespace?
01891   char          *s,                                     // Pointer into buffer
01892                 buf[1024],                              // Text buffer
01893                 attr[1024],                             // Other attribute
01894                 wattr[1024],                            // WIDTH attribute
01895                 hattr[1024];                            // HEIGHT attribute
01896   const char    *ptr,                                   // Pointer into table
01897                 *attrs,                                 // Pointer to attributes
01898                 *start;                                 // Start of element
01899   int           minwidths[MAX_COLUMNS];                 // Minimum widths for each column
01900   Fl_Font       font;
01901   Fl_Fontsize   fsize;                                  // Current font and size
01902   Fl_Color      fcolor;                                 // Currrent font color
01903 
01904   // Clear widths...
01905   *table_width = 0;
01906   for (column = 0; column < MAX_COLUMNS; column ++)
01907   {
01908     columns[column]   = 0;
01909     minwidths[column] = 0;
01910   }
01911 
01912   num_columns = 0;
01913   colspan     = 0;
01914   max_width   = 0;
01915   pre         = 0;
01916   needspace   = 0;
01917   fstack_.top(font, fsize, fcolor);
01918 
01919   // Scan the table...
01920   for (ptr = table, column = -1, width = 0, s = buf, incell = 0; *ptr;)
01921   {
01922     if ((*ptr == '<' || isspace((*ptr)&255)) && s > buf && incell)
01923     {
01924       // Check width...
01925       if (needspace)
01926       {
01927         *s++      = ' ';
01928         needspace = 0;
01929       }
01930 
01931       *s         = '\0';
01932       temp_width = (int)fl_width(buf);
01933       s          = buf;
01934 
01935       if (temp_width > minwidths[column])
01936         minwidths[column] = temp_width;
01937 
01938       width += temp_width;
01939 
01940       if (width > max_width)
01941         max_width = width;
01942     }
01943 
01944     if (*ptr == '<')
01945     {
01946       start = ptr;
01947 
01948       for (s = buf, ptr ++; *ptr && *ptr != '>' && !isspace((*ptr)&255);)
01949         if (s < (buf + sizeof(buf) - 1))
01950           *s++ = *ptr++;
01951         else
01952           ptr ++;
01953 
01954       *s = '\0';
01955       s = buf;
01956 
01957       attrs = ptr;
01958       while (*ptr && *ptr != '>')
01959         ptr ++;
01960 
01961       if (*ptr == '>')
01962         ptr ++;
01963 
01964       if (strcasecmp(buf, "BR") == 0 ||
01965           strcasecmp(buf, "HR") == 0)
01966       {
01967         width     = 0;
01968         needspace = 0;
01969       }
01970       else if (strcasecmp(buf, "TABLE") == 0 && start > table)
01971         break;
01972       else if (strcasecmp(buf, "CENTER") == 0 ||
01973                strcasecmp(buf, "P") == 0 ||
01974                strcasecmp(buf, "H1") == 0 ||
01975                strcasecmp(buf, "H2") == 0 ||
01976                strcasecmp(buf, "H3") == 0 ||
01977                strcasecmp(buf, "H4") == 0 ||
01978                strcasecmp(buf, "H5") == 0 ||
01979                strcasecmp(buf, "H6") == 0 ||
01980                strcasecmp(buf, "UL") == 0 ||
01981                strcasecmp(buf, "OL") == 0 ||
01982                strcasecmp(buf, "DL") == 0 ||
01983                strcasecmp(buf, "LI") == 0 ||
01984                strcasecmp(buf, "DD") == 0 ||
01985                strcasecmp(buf, "DT") == 0 ||
01986                strcasecmp(buf, "PRE") == 0)
01987       {
01988         width     = 0;
01989         needspace = 0;
01990 
01991         if (tolower(buf[0]) == 'h' && isdigit(buf[1]))
01992         {
01993           font  = FL_HELVETICA_BOLD;
01994           fsize = textsize_ + '7' - buf[1];
01995         }
01996         else if (strcasecmp(buf, "DT") == 0)
01997         {
01998           font  = textfont_ | FL_ITALIC;
01999           fsize = textsize_;
02000         }
02001         else if (strcasecmp(buf, "PRE") == 0)
02002         {
02003           font  = FL_COURIER;
02004           fsize = textsize_;
02005           pre   = 1;
02006         }
02007         else if (strcasecmp(buf, "LI") == 0)
02008         {
02009           width  += 4 * fsize;
02010           font   = textfont_;
02011           fsize  = textsize_;
02012         }
02013         else
02014         {
02015           font  = textfont_;
02016           fsize = textsize_;
02017         }
02018 
02019         pushfont(font, fsize);
02020       }
02021       else if (strcasecmp(buf, "/CENTER") == 0 ||
02022                strcasecmp(buf, "/P") == 0 ||
02023                strcasecmp(buf, "/H1") == 0 ||
02024                strcasecmp(buf, "/H2") == 0 ||
02025                strcasecmp(buf, "/H3") == 0 ||
02026                strcasecmp(buf, "/H4") == 0 ||
02027                strcasecmp(buf, "/H5") == 0 ||
02028                strcasecmp(buf, "/H6") == 0 ||
02029                strcasecmp(buf, "/PRE") == 0 ||
02030                strcasecmp(buf, "/UL") == 0 ||
02031                strcasecmp(buf, "/OL") == 0 ||
02032                strcasecmp(buf, "/DL") == 0)
02033       {
02034         width     = 0;
02035         needspace = 0;
02036 
02037         popfont(font, fsize, fcolor);
02038       }
02039       else if (strcasecmp(buf, "TR") == 0 || strcasecmp(buf, "/TR") == 0 ||
02040                strcasecmp(buf, "/TABLE") == 0)
02041       {
02042 //        printf("%s column = %d, colspan = %d, num_columns = %d\n",
02043 //             buf, column, colspan, num_columns);
02044 
02045         if (column >= 0)
02046         {
02047           // This is a hack to support COLSPAN...
02048           max_width /= colspan;
02049 
02050           while (colspan > 0)
02051           {
02052             if (max_width > columns[column])
02053               columns[column] = max_width;
02054 
02055             column ++;
02056             colspan --;
02057           }
02058         }
02059 
02060         if (strcasecmp(buf, "/TABLE") == 0)
02061           break;
02062 
02063         needspace = 0;
02064         column    = -1;
02065         width     = 0;
02066         max_width = 0;
02067         incell    = 0;
02068       }
02069       else if (strcasecmp(buf, "TD") == 0 ||
02070                strcasecmp(buf, "TH") == 0)
02071       {
02072 //        printf("BEFORE column = %d, colspan = %d, num_columns = %d\n",
02073 //             column, colspan, num_columns);
02074 
02075         if (column >= 0)
02076         {
02077           // This is a hack to support COLSPAN...
02078           max_width /= colspan;
02079 
02080           while (colspan > 0)
02081           {
02082             if (max_width > columns[column])
02083               columns[column] = max_width;
02084 
02085             column ++;
02086             colspan --;
02087           }
02088         }
02089         else
02090           column ++;
02091 
02092         if (get_attr(attrs, "COLSPAN", attr, sizeof(attr)) != NULL)
02093           colspan = atoi(attr);
02094         else
02095           colspan = 1;
02096 
02097 //        printf("AFTER column = %d, colspan = %d, num_columns = %d\n",
02098 //             column, colspan, num_columns);
02099 
02100         if ((column + colspan) >= num_columns)
02101           num_columns = column + colspan;
02102 
02103         needspace = 0;
02104         width     = 0;
02105         incell    = 1;
02106 
02107         if (strcasecmp(buf, "TH") == 0)
02108           font = textfont_ | FL_BOLD;
02109         else
02110           font = textfont_;
02111 
02112         fsize = textsize_;
02113 
02114         pushfont(font, fsize);
02115 
02116         if (get_attr(attrs, "WIDTH", attr, sizeof(attr)) != NULL)
02117           max_width = get_length(attr);
02118         else
02119           max_width = 0;
02120 
02121 //        printf("max_width = %d\n", max_width);
02122       }
02123       else if (strcasecmp(buf, "/TD") == 0 ||
02124                strcasecmp(buf, "/TH") == 0)
02125       {
02126         incell = 0;
02127         popfont(font, fsize, fcolor);
02128       }
02129       else if (strcasecmp(buf, "B") == 0 ||
02130                strcasecmp(buf, "STRONG") == 0)
02131         pushfont(font |= FL_BOLD, fsize);
02132       else if (strcasecmp(buf, "I") == 0 ||
02133                strcasecmp(buf, "EM") == 0)
02134         pushfont(font |= FL_ITALIC, fsize);
02135       else if (strcasecmp(buf, "CODE") == 0 ||
02136                strcasecmp(buf, "TT") == 0)
02137         pushfont(font = FL_COURIER, fsize);
02138       else if (strcasecmp(buf, "KBD") == 0)
02139         pushfont(font = FL_COURIER_BOLD, fsize);
02140       else if (strcasecmp(buf, "VAR") == 0)
02141         pushfont(font = FL_COURIER_ITALIC, fsize);
02142       else if (strcasecmp(buf, "/B") == 0 ||
02143                strcasecmp(buf, "/STRONG") == 0 ||
02144                strcasecmp(buf, "/I") == 0 ||
02145                strcasecmp(buf, "/EM") == 0 ||
02146                strcasecmp(buf, "/CODE") == 0 ||
02147                strcasecmp(buf, "/TT") == 0 ||
02148                strcasecmp(buf, "/KBD") == 0 ||
02149                strcasecmp(buf, "/VAR") == 0)
02150         popfont(font, fsize, fcolor);
02151       else if (strcasecmp(buf, "IMG") == 0 && incell)
02152       {
02153         Fl_Shared_Image *img = 0;
02154         int             iwidth, iheight;
02155 
02156 
02157         get_attr(attrs, "WIDTH", wattr, sizeof(wattr));
02158         get_attr(attrs, "HEIGHT", hattr, sizeof(hattr));
02159         iwidth  = get_length(wattr);
02160         iheight = get_length(hattr);
02161 
02162         if (get_attr(attrs, "SRC", attr, sizeof(attr))) {
02163           img     = get_image(attr, iwidth, iheight);
02164           iwidth  = img->w();
02165           iheight = img->h();
02166         }
02167 
02168         if (iwidth > minwidths[column])
02169           minwidths[column] = iwidth;
02170 
02171         width += iwidth;
02172         if (needspace)
02173           width += (int)fl_width(' ');
02174 
02175         if (width > max_width)
02176           max_width = width;
02177 
02178         needspace = 0;
02179       }
02180     }
02181     else if (*ptr == '\n' && pre)
02182     {
02183       width     = 0;
02184       needspace = 0;
02185       ptr ++;
02186     }
02187     else if (isspace((*ptr)&255))
02188     {
02189       needspace = 1;
02190 
02191       ptr ++;
02192     }
02193     else if (*ptr == '&' && s < (buf + sizeof(buf) - 1))
02194     {
02195       ptr ++;
02196 
02197       int qch = quote_char(ptr);
02198 
02199       if (qch < 0)
02200         *s++ = '&';
02201       else {
02202 //        int l;
02203 //        l = fl_utf8encode((unsigned int) qch, s);
02204 //        if (l < 1) l = 1;
02205 //        s += l;
02206         *s++ = qch;
02207         ptr = strchr(ptr, ';') + 1;
02208       }
02209     }
02210     else
02211     {
02212       if (s < (buf + sizeof(buf) - 1))
02213         *s++ = *ptr++;
02214       else
02215         ptr ++;
02216     }
02217   }
02218 
02219   // Now that we have scanned the entire table, adjust the table and
02220   // cell widths to fit on the screen...
02221   if (get_attr(table + 6, "WIDTH", attr, sizeof(attr)))
02222     *table_width = get_length(attr);
02223   else
02224     *table_width = 0;
02225 
02226 #ifdef DEBUG
02227   printf("num_columns = %d, table_width = %d\n", num_columns, *table_width);
02228 #endif // DEBUG
02229 
02230   if (num_columns == 0)
02231     return;
02232 
02233   // Add up the widths...
02234   for (column = 0, width = 0; column < num_columns; column ++)
02235     width += columns[column];
02236 
02237 #ifdef DEBUG
02238   printf("width = %d, w() = %d\n", width, w());
02239   for (column = 0; column < num_columns; column ++)
02240     printf("    columns[%d] = %d, minwidths[%d] = %d\n", column, columns[column],
02241            column, minwidths[column]);
02242 #endif // DEBUG
02243 
02244   // Adjust the width if needed...
02245   int scale_width = *table_width;
02246 
02247   int scrollsize = scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size();
02248   if (scale_width == 0) {
02249     if (width > (hsize_ - scrollsize)) scale_width = hsize_ - scrollsize;
02250     else scale_width = width;
02251   }
02252 
02253   if (width < scale_width) {
02254 #ifdef DEBUG
02255     printf("Scaling table up to %d from %d...\n", scale_width, width);
02256 #endif // DEBUG
02257 
02258     *table_width = 0;
02259 
02260     scale_width = (scale_width - width) / num_columns;
02261 
02262 #ifdef DEBUG
02263     printf("adjusted scale_width = %d\n", scale_width);
02264 #endif // DEBUG
02265 
02266     for (column = 0; column < num_columns; column ++) {
02267       columns[column] += scale_width;
02268 
02269       (*table_width) += columns[column];
02270     }
02271   }
02272   else if (width > scale_width) {
02273 #ifdef DEBUG
02274     printf("Scaling table down to %d from %d...\n", scale_width, width);
02275 #endif // DEBUG
02276 
02277     for (column = 0; column < num_columns; column ++) {
02278       width       -= minwidths[column];
02279       scale_width -= minwidths[column];
02280     }
02281 
02282 #ifdef DEBUG
02283     printf("adjusted width = %d, scale_width = %d\n", width, scale_width);
02284 #endif // DEBUG
02285 
02286     if (width > 0) {
02287       for (column = 0; column < num_columns; column ++) {
02288         columns[column] -= minwidths[column];
02289         columns[column] = scale_width * columns[column] / width;
02290         columns[column] += minwidths[column];
02291       }
02292     }
02293 
02294     *table_width = 0;
02295     for (column = 0; column < num_columns; column ++) {
02296       (*table_width) += columns[column];
02297     }
02298   }
02299   else if (*table_width == 0)
02300     *table_width = width;
02301 
02302 #ifdef DEBUG
02303   printf("FINAL table_width = %d\n", *table_width);
02304   for (column = 0; column < num_columns; column ++)
02305     printf("    columns[%d] = %d\n", column, columns[column]);
02306 #endif // DEBUG
02307 }
02308 
02309 
02311 void
02312 Fl_Help_View::free_data() {
02313   // Release all images...
02314   if (value_) {
02315     const char  *ptr,           // Pointer into block
02316                 *attrs;         // Pointer to start of element attributes
02317     char        *s,             // Pointer into buffer
02318                 buf[1024],      // Text buffer
02319                 attr[1024],     // Attribute buffer
02320                 wattr[1024],    // Width attribute buffer
02321                 hattr[1024];    // Height attribute buffer
02322 
02323     for (ptr = value_; *ptr;)
02324     {
02325       if (*ptr == '<')
02326       {
02327         ptr ++;
02328 
02329         if (strncmp(ptr, "!--", 3) == 0)
02330         {
02331           // Comment...
02332           ptr += 3;
02333           if ((ptr = strstr(ptr, "-->")) != NULL)
02334           {
02335             ptr += 3;
02336             continue;
02337           }
02338           else
02339             break;
02340         }
02341 
02342         s = buf;
02343 
02344         while (*ptr && *ptr != '>' && !isspace((*ptr)&255))
02345           if (s < (buf + sizeof(buf) - 1))
02346             *s++ = *ptr++;
02347           else
02348             ptr ++;
02349 
02350         *s = '\0';
02351 
02352         attrs = ptr;
02353         while (*ptr && *ptr != '>')
02354           ptr ++;
02355 
02356         if (*ptr == '>')
02357           ptr ++;
02358 
02359         if (strcasecmp(buf, "IMG") == 0)
02360         {
02361           Fl_Shared_Image       *img;
02362           int           width;
02363           int           height;
02364 
02365           get_attr(attrs, "WIDTH", wattr, sizeof(wattr));
02366           get_attr(attrs, "HEIGHT", hattr, sizeof(hattr));
02367           width  = get_length(wattr);
02368           height = get_length(hattr);
02369 
02370           if (get_attr(attrs, "SRC", attr, sizeof(attr))) {
02371             // Get and release the image to free it from memory...
02372             img = get_image(attr, width, height);
02373             if ((void*)img != &broken_image) {
02374               img->release();
02375             }
02376           }
02377         }
02378       }
02379       else
02380         ptr ++;
02381     }
02382 
02383     free((void *)value_);
02384     value_ = 0;
02385   }
02386 
02387   // Free all of the arrays...
02388   if (nblocks_) {
02389     free(blocks_);
02390 
02391     ablocks_ = 0;
02392     nblocks_ = 0;
02393     blocks_  = 0;
02394   }
02395 
02396   if (nlinks_) {
02397     free(links_);
02398 
02399     alinks_ = 0;
02400     nlinks_ = 0;
02401     links_  = 0;
02402   }
02403 
02404   if (ntargets_) {
02405     free(targets_);
02406 
02407     atargets_ = 0;
02408     ntargets_ = 0;
02409     targets_  = 0;
02410   }
02411 }
02412 
02414 int                                     // O - Alignment
02415 Fl_Help_View::get_align(const char *p,  // I - Pointer to start of attrs
02416                         int        a)   // I - Default alignment
02417 {
02418   char  buf[255];                       // Alignment value
02419 
02420 
02421   if (get_attr(p, "ALIGN", buf, sizeof(buf)) == NULL)
02422     return (a);
02423 
02424   if (strcasecmp(buf, "CENTER") == 0)
02425     return (CENTER);
02426   else if (strcasecmp(buf, "RIGHT") == 0)
02427     return (RIGHT);
02428   else
02429     return (LEFT);
02430 }
02431 
02432 
02434 const char *                                    // O - Pointer to buf or NULL
02435 Fl_Help_View::get_attr(const char *p,           // I - Pointer to start of attributes
02436                       const char *n,            // I - Name of attribute
02437                       char       *buf,          // O - Buffer for attribute value
02438                       int        bufsize)       // I - Size of buffer
02439 {
02440   char  name[255],                              // Name from string
02441         *ptr,                                   // Pointer into name or value
02442         quote;                                  // Quote
02443 
02444 
02445   buf[0] = '\0';
02446 
02447   while (*p && *p != '>')
02448   {
02449     while (isspace((*p)&255))
02450       p ++;
02451 
02452     if (*p == '>' || !*p)
02453       return (NULL);
02454 
02455     for (ptr = name; *p && !isspace((*p)&255) && *p != '=' && *p != '>';)
02456       if (ptr < (name + sizeof(name) - 1))
02457         *ptr++ = *p++;
02458       else
02459         p ++;
02460 
02461     *ptr = '\0';
02462 
02463     if (isspace((*p)&255) || !*p || *p == '>')
02464       buf[0] = '\0';
02465     else
02466     {
02467       if (*p == '=')
02468         p ++;
02469 
02470       for (ptr = buf; *p && !isspace((*p)&255) && *p != '>';)
02471         if (*p == '\'' || *p == '\"')
02472         {
02473           quote = *p++;
02474 
02475           while (*p && *p != quote)
02476             if ((ptr - buf + 1) < bufsize)
02477               *ptr++ = *p++;
02478             else
02479               p ++;
02480 
02481           if (*p == quote)
02482             p ++;
02483         }
02484         else if ((ptr - buf + 1) < bufsize)
02485           *ptr++ = *p++;
02486         else
02487           p ++;
02488 
02489       *ptr = '\0';
02490     }
02491 
02492     if (strcasecmp(n, name) == 0)
02493       return (buf);
02494     else
02495       buf[0] = '\0';
02496 
02497     if (*p == '>')
02498       return (NULL);
02499   }
02500 
02501   return (NULL);
02502 }
02503 
02504 
02506 Fl_Color                                // O - Color value
02507 Fl_Help_View::get_color(const char *n,  // I - Color name
02508                         Fl_Color   c)   // I - Default color value
02509 {
02510   int   i;                              // Looping var
02511   int   rgb, r, g, b;                   // RGB values
02512   static const struct {                 // Color name table
02513     const char *name;
02514     int r, g, b;
02515   }     colors[] = {
02516     { "black",          0x00, 0x00, 0x00 },
02517     { "red",            0xff, 0x00, 0x00 },
02518     { "green",          0x00, 0x80, 0x00 },
02519     { "yellow",         0xff, 0xff, 0x00 },
02520     { "blue",           0x00, 0x00, 0xff },
02521     { "magenta",        0xff, 0x00, 0xff },
02522     { "fuchsia",        0xff, 0x00, 0xff },
02523     { "cyan",           0x00, 0xff, 0xff },
02524     { "aqua",           0x00, 0xff, 0xff },
02525     { "white",          0xff, 0xff, 0xff },
02526     { "gray",           0x80, 0x80, 0x80 },
02527     { "grey",           0x80, 0x80, 0x80 },
02528     { "lime",           0x00, 0xff, 0x00 },
02529     { "maroon",         0x80, 0x00, 0x00 },
02530     { "navy",           0x00, 0x00, 0x80 },
02531     { "olive",          0x80, 0x80, 0x00 },
02532     { "purple",         0x80, 0x00, 0x80 },
02533     { "silver",         0xc0, 0xc0, 0xc0 },
02534     { "teal",           0x00, 0x80, 0x80 }
02535   };
02536 
02537 
02538   if (!n || !n[0]) return c;
02539 
02540   if (n[0] == '#') {
02541     // Do hex color lookup
02542     rgb = strtol(n + 1, NULL, 16);
02543 
02544     if (strlen(n) > 4) {
02545       r = rgb >> 16;
02546       g = (rgb >> 8) & 255;
02547       b = rgb & 255;
02548     } else {
02549       r = (rgb >> 8) * 17;
02550       g = ((rgb >> 4) & 15) * 17;
02551       b = (rgb & 15) * 17;
02552     }
02553     return (fl_rgb_color((uchar)r, (uchar)g, (uchar)b));
02554   } else {
02555     for (i = 0; i < (int)(sizeof(colors) / sizeof(colors[0])); i ++)
02556       if (!strcasecmp(n, colors[i].name)) {
02557         return fl_rgb_color(colors[i].r, colors[i].g, colors[i].b);
02558       }
02559     return c;
02560   }
02561 }
02562 
02563 
02577 /* Implementation note: (A.S. Apr 05, 2009)
02578 
02579   Fl_Help_View::get_image() uses a static global flag (initial_load)
02580   to determine, if it is called from the initial loading of a document
02581   (load() or value()), or from resize() or draw().
02582 
02583   A better solution would be to manage all loaded images in an own
02584   structure like Fl_Help_Target (Fl_Help_Image ?) to avoid using this
02585   global flag, but this would break the ABI !
02586 
02587   This should be fixed in FLTK 1.3 !
02588 
02589 
02590   If initial_load is true, then Fl_Shared_Image::get() is called to
02591   load the image, and the reference count of the shared image is
02592   increased by one.
02593 
02594   If initial_load is false, then Fl_Shared_Image::find() is called to
02595   load the image, and the image is released immediately. This avoids
02596   increasing the reference count when calling get_image() from draw()
02597   or resize().
02598 
02599   Calling Fl_Shared_Image::find() instead of Fl_Shared_Image::get() avoids
02600   doing unnecessary i/o for "broken images" within each resize/redraw.
02601 
02602   Each image must be released exactly once in the destructor or before
02603   a new document is loaded: see free_data().
02604 */
02605 
02606 Fl_Shared_Image *
02607 Fl_Help_View::get_image(const char *name, int W, int H) {
02608   const char    *localname;             // Local filename
02609   char          dir[FL_PATH_MAX];       // Current directory
02610   char          temp[FL_PATH_MAX],      // Temporary filename
02611                 *tempptr;               // Pointer into temporary name
02612   Fl_Shared_Image *ip;                  // Image pointer...
02613 
02614   // See if the image can be found...
02615   if (strchr(directory_, ':') != NULL && strchr(name, ':') == NULL) {
02616     if (name[0] == '/') {
02617       strlcpy(temp, directory_, sizeof(temp));
02618 
02619       if ((tempptr = strrchr(strchr(directory_, ':') + 3, '/')) != NULL) {
02620         strlcpy(tempptr, name, sizeof(temp) - (tempptr - temp));
02621       } else {
02622         strlcat(temp, name, sizeof(temp));
02623       }
02624     } else {
02625       snprintf(temp, sizeof(temp), "%s/%s", directory_, name);
02626     }
02627 
02628     if (link_) localname = (*link_)(this, temp);
02629     else localname = temp;
02630   } else if (name[0] != '/' && strchr(name, ':') == NULL) {
02631     if (directory_[0]) snprintf(temp, sizeof(temp), "%s/%s", directory_, name);
02632     else {
02633       fl_getcwd(dir, sizeof(dir));
02634       snprintf(temp, sizeof(temp), "file:%s/%s", dir, name);
02635     }
02636 
02637     if (link_) localname = (*link_)(this, temp);
02638     else localname = temp;
02639   } else if (link_) localname = (*link_)(this, name);
02640   else localname = name;
02641 
02642   if (!localname) return 0;
02643 
02644   if (strncmp(localname, "file:", 5) == 0) localname += 5;
02645 
02646   if (initial_load) {
02647     if ((ip = Fl_Shared_Image::get(localname, W, H)) == NULL) {
02648       ip = (Fl_Shared_Image *)&broken_image;
02649     }
02650   } else { // draw or resize
02651     if ((ip = Fl_Shared_Image::find(localname, W, H)) == NULL) {
02652       ip = (Fl_Shared_Image *)&broken_image;
02653     } else {
02654       ip->release();
02655     }
02656   }
02657 
02658   return ip;
02659 }
02660 
02661 
02663 int
02664 Fl_Help_View::get_length(const char *l) {       // I - Value
02665   int   val;                                    // Integer value
02666 
02667   if (!l[0]) return 0;
02668 
02669   val = atoi(l);
02670   if (l[strlen(l) - 1] == '%') {
02671     if (val > 100) val = 100;
02672     else if (val < 0) val = 0;
02673 
02674     int scrollsize = scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size();
02675     val = val * (hsize_ - scrollsize) / 100;
02676   }
02677 
02678   return val;
02679 }
02680 
02681 
02682 Fl_Help_Link *Fl_Help_View::find_link(int xx, int yy)
02683 {
02684   int           i;
02685   Fl_Help_Link  *linkp;
02686   for (i = nlinks_, linkp = links_; i > 0; i --, linkp ++) {
02687     if (xx >= linkp->x && xx < linkp->w &&
02688         yy >= linkp->y && yy < linkp->h)
02689       break;
02690   }
02691   return i ? linkp : 0L;
02692 }
02693 
02694 void Fl_Help_View::follow_link(Fl_Help_Link *linkp)
02695 {
02696   char          target[32];     // Current target
02697 
02698   clear_selection();
02699 
02700   strlcpy(target, linkp->name, sizeof(target));
02701 
02702   set_changed();
02703 
02704   if (strcmp(linkp->filename, filename_) != 0 && linkp->filename[0])
02705   {
02706     char        dir[FL_PATH_MAX];       // Current directory
02707     char        temp[FL_PATH_MAX],      // Temporary filename
02708               *tempptr; // Pointer into temporary filename
02709 
02710 
02711     if (strchr(directory_, ':') != NULL &&
02712         strchr(linkp->filename, ':') == NULL)
02713     {
02714       if (linkp->filename[0] == '/')
02715       {
02716         strlcpy(temp, directory_, sizeof(temp));
02717         if ((tempptr = strrchr(strchr(directory_, ':') + 3, '/')) != NULL)
02718           strlcpy(tempptr, linkp->filename, sizeof(temp));
02719         else
02720           strlcat(temp, linkp->filename, sizeof(temp));
02721       }
02722       else
02723         snprintf(temp, sizeof(temp), "%s/%s", directory_, linkp->filename);
02724     }
02725     else if (linkp->filename[0] != '/' && strchr(linkp->filename, ':') == NULL)
02726     {
02727       if (directory_[0])
02728         snprintf(temp, sizeof(temp), "%s/%s", directory_, linkp->filename);
02729       else
02730       {
02731           fl_getcwd(dir, sizeof(dir));
02732         snprintf(temp, sizeof(temp), "file:%s/%s", dir, linkp->filename);
02733       }
02734     }
02735     else
02736       strlcpy(temp, linkp->filename, sizeof(temp));
02737 
02738     if (linkp->name[0])
02739       snprintf(temp + strlen(temp), sizeof(temp) - strlen(temp), "#%s",
02740                linkp->name);
02741 
02742     load(temp);
02743   }
02744   else if (target[0])
02745     topline(target);
02746   else
02747     topline(0);
02748 
02749   leftline(0);
02750 }
02751 
02753 void Fl_Help_View::clear_selection()
02754 {
02755   if (current_view==this)
02756     clear_global_selection();
02757 }
02759 void Fl_Help_View::select_all()
02760 {
02761   clear_global_selection();
02762   if (!value_) return;
02763   current_view = this;
02764   selection_drag_last = selection_last = strlen(value_);
02765   selected = 1;
02766 }
02767 
02768 void Fl_Help_View::clear_global_selection()
02769 {
02770   if (selected) redraw();
02771   selection_push_first = selection_push_last = 0;
02772   selection_drag_first = selection_drag_last = 0;
02773   selection_first = selection_last = 0;
02774   selected = 0;
02775 }
02776 
02777 char Fl_Help_View::begin_selection()
02778 {
02779   clear_global_selection();
02780 
02781   if (!fl_help_view_buffer) fl_help_view_buffer = fl_create_offscreen(1, 1);
02782 
02783   mouse_x = Fl::event_x();
02784   mouse_y = Fl::event_y();
02785   draw_mode = 1;
02786 
02787     current_view = this;
02788     fl_begin_offscreen(fl_help_view_buffer);
02789     draw();
02790     fl_end_offscreen();
02791 
02792   draw_mode = 0;
02793 
02794   if (selection_push_last) return 1;
02795   else return 0;
02796 }
02797 
02798 char Fl_Help_View::extend_selection()
02799 {
02800   if (Fl::event_is_click())
02801     return 0;
02802 
02803 //  printf("old selection_first=%d, selection_last=%d\n",
02804 //         selection_first, selection_last);
02805 
02806   int sf = selection_first, sl = selection_last;
02807 
02808   selected = 1;
02809   mouse_x = Fl::event_x();
02810   mouse_y = Fl::event_y();
02811   draw_mode = 2;
02812 
02813     fl_begin_offscreen(fl_help_view_buffer);
02814     draw();
02815     fl_end_offscreen();
02816 
02817   draw_mode = 0;
02818 
02819   if (selection_push_first < selection_drag_first) {
02820     selection_first = selection_push_first;
02821   } else {
02822     selection_first = selection_drag_first;
02823   }
02824 
02825   if (selection_push_last > selection_drag_last) {
02826     selection_last = selection_push_last;
02827   } else {
02828     selection_last = selection_drag_last;
02829   }
02830 
02831 //  printf("new selection_first=%d, selection_last=%d\n",
02832 //         selection_first, selection_last);
02833 
02834   if (sf!=selection_first || sl!=selection_last) {
02835 //    puts("REDRAW!!!\n");
02836     return 1;
02837   } else {
02838 //    puts("");
02839     return 0;
02840   }
02841 }
02842 
02843 // convert a command with up to four letters into an unsigned int
02844 static unsigned int command(const char *cmd)
02845 {
02846   unsigned int ret = (tolower(cmd[0])<<24);
02847   char c = cmd[1];
02848   if (c=='>' || c==' ' || c==0) return ret;
02849   ret |= (tolower(c)<<16);
02850   c = cmd[2];
02851   if (c=='>' || c==' ' || c==0) return ret;
02852   ret |= (tolower(c)<<8);
02853   c = cmd[3];
02854   if (c=='>' || c==' ' || c==0) return ret;
02855   ret |= tolower(c);
02856   c = cmd[4];
02857   if (c=='>' || c==' ' || c==0) return ret;
02858   return 0;
02859 }
02860 
02861 #define CMD(a, b, c, d) ((a<<24)|(b<<16)|(c<<8)|d)
02862 
02863 void Fl_Help_View::end_selection(int clipboard) 
02864 {
02865   if (!selected || current_view!=this) 
02866     return;
02867   // convert the select part of our html text into some kind of somewhat readable ASCII
02868   // and store it in the selection buffer
02869   char p = 0, pre = 0;;
02870   int len = strlen(value_);
02871   char *txt = (char*)malloc(len+1), *d = txt;
02872   const char *s = value_, *cmd, *src;
02873   for (;;) {
02874     char c = *s++;
02875     if (c==0) break;
02876     if (c=='<') { // begin of some html command. Skip until we find a '>'
02877       cmd = s;
02878       for (;;) {
02879         c = *s++;
02880         if (c==0 || c=='>') break;
02881       }
02882       if (c==0) break;
02883       // do something with this command... .
02884       // the replacement string must not be longer that the command itself plus '<' and '>'
02885       src = 0;
02886       switch (command(cmd)) {
02887         case CMD('p','r','e', 0 ): pre = 1; break;
02888         case CMD('/','p','r','e'): pre = 0; break;
02889         case CMD('t','d', 0 , 0 ):
02890         case CMD('p', 0 , 0 , 0 ):
02891         case CMD('/','p', 0 , 0 ):
02892         case CMD('b','r', 0 , 0 ): src = "\n"; break;
02893         case CMD('l','i', 0 , 0 ): src = "\n * "; break;
02894         case CMD('/','h','1', 0 ):
02895         case CMD('/','h','2', 0 ):
02896         case CMD('/','h','3', 0 ):
02897         case CMD('/','h','4', 0 ):
02898         case CMD('/','h','5', 0 ):
02899         case CMD('/','h','6', 0 ): src = "\n\n"; break;
02900         case CMD('t','r', 0 , 0 ):
02901         case CMD('h','1', 0 , 0 ):
02902         case CMD('h','2', 0 , 0 ):
02903         case CMD('h','3', 0 , 0 ):
02904         case CMD('h','4', 0 , 0 ):
02905         case CMD('h','5', 0 , 0 ):
02906         case CMD('h','6', 0 , 0 ): src = "\n\n"; break;
02907         case CMD('d','t', 0 , 0 ): src = "\n "; break;
02908         case CMD('d','d', 0 , 0 ): src = "\n - "; break;
02909       }
02910       int n = s-value_;
02911       if (src && n>selection_first && n<=selection_last) {
02912         while (*src) {
02913           *d++ = *src++;
02914         }
02915         c = src[-1];
02916         p = isspace(c&255) ? ' ' : c;
02917       }
02918       continue;
02919     }
02920     if (c=='&') { // special characters
02921       int xx = quote_char(s);
02922       if (xx>=0) {
02923         c = (char)xx;
02924         for (;;) {
02925           char cc = *s++;
02926           if (!cc || cc==';') break;
02927         }
02928       }
02929     }
02930     int n = s-value_;
02931     if (n>selection_first && n<=selection_last) {
02932       if (!pre && isspace(c&255)) c = ' ';
02933       if (p!=' '||c!=' ')
02934         *d++ = c;
02935       p = c;
02936     }
02937   }
02938   *d = 0;
02939   Fl::copy(txt, strlen(txt), clipboard);
02940   free(txt);
02941 }
02942 
02943 #define ctrl(x) ((x)&0x1f)
02944 
02946 int                             // O - 1 if we handled it, 0 otherwise
02947 Fl_Help_View::handle(int event) // I - Event to handle
02948 {
02949   static Fl_Help_Link *linkp;   // currently clicked link
02950 
02951   int xx = Fl::event_x() - x() + leftline_;
02952   int yy = Fl::event_y() - y() + topline_;
02953 
02954   switch (event)
02955   {
02956     case FL_FOCUS:
02957       redraw();
02958       return 1;
02959     case FL_UNFOCUS:
02960       clear_selection();
02961       redraw();
02962       return 1;
02963     case FL_ENTER :
02964       Fl_Group::handle(event);
02965       return 1;
02966     case FL_LEAVE :
02967       fl_cursor(FL_CURSOR_DEFAULT);
02968       break;
02969     case FL_MOVE:
02970       if (find_link(xx, yy)) fl_cursor(FL_CURSOR_HAND);
02971       else fl_cursor(FL_CURSOR_DEFAULT);
02972       return 1;
02973     case FL_PUSH:
02974       if (Fl_Group::handle(event)) return 1;
02975       linkp = find_link(xx, yy);
02976       if (linkp) {
02977         fl_cursor(FL_CURSOR_HAND);
02978         return 1;
02979       }
02980       if (begin_selection()) {
02981         fl_cursor(FL_CURSOR_INSERT);
02982         return 1;
02983       }
02984       fl_cursor(FL_CURSOR_DEFAULT);
02985       return 1;
02986     case FL_DRAG:
02987       if (linkp) {
02988         if (Fl::event_is_click()) {
02989           fl_cursor(FL_CURSOR_HAND);
02990         } else {
02991           fl_cursor(FL_CURSOR_DEFAULT); // should be "FL_CURSOR_CANCEL" if we had it
02992         }
02993         return 1;
02994       }
02995       if (current_view==this && selection_push_last) {
02996         if (extend_selection()) redraw();
02997         fl_cursor(FL_CURSOR_INSERT);
02998         return 1;
02999       }
03000       fl_cursor(FL_CURSOR_DEFAULT);
03001       return 1;
03002     case FL_RELEASE:
03003       if (linkp) {
03004         if (Fl::event_is_click()) {
03005           follow_link(linkp);
03006         }
03007         fl_cursor(FL_CURSOR_DEFAULT);
03008         linkp = 0;
03009         return 1;
03010       }
03011       if (current_view==this && selection_push_last) {
03012         end_selection();
03013         return 1;
03014       }
03015       return 1;
03016     case FL_SHORTCUT: {
03017       char ascii = Fl::event_text()[0];
03018       switch (ascii) {
03019         case ctrl('A'): select_all(); redraw(); return 1;
03020         case ctrl('C'):
03021         case ctrl('X'): end_selection(1); return 1;
03022       }
03023       break; }
03024   }
03025   return (Fl_Group::handle(event));
03026 }
03027 
03032 Fl_Help_View::Fl_Help_View(int        xx,       // I - Left position
03033                            int        yy,       // I - Top position
03034                            int        ww,       // I - Width in pixels
03035                            int        hh,       // I - Height in pixels
03036                            const char *l)
03037     : Fl_Group(xx, yy, ww, hh, l),
03038       scrollbar_(xx + ww - Fl::scrollbar_size(), yy,
03039                  Fl::scrollbar_size(), hh - Fl::scrollbar_size()),
03040       hscrollbar_(xx, yy + hh - Fl::scrollbar_size(),
03041                   ww - Fl::scrollbar_size(), Fl::scrollbar_size())
03042 {
03043   color(FL_BACKGROUND2_COLOR, FL_SELECTION_COLOR);
03044 
03045   title_[0]     = '\0';
03046   defcolor_     = FL_FOREGROUND_COLOR;
03047   bgcolor_      = FL_BACKGROUND_COLOR;
03048   textcolor_    = FL_FOREGROUND_COLOR;
03049   linkcolor_    = FL_SELECTION_COLOR;
03050   textfont_     = FL_TIMES;
03051   textsize_     = 12;
03052   value_        = NULL;
03053 
03054   ablocks_      = 0;
03055   nblocks_      = 0;
03056   blocks_       = (Fl_Help_Block *)0;
03057 
03058   link_         = (Fl_Help_Func *)0;
03059 
03060   alinks_       = 0;
03061   nlinks_       = 0;
03062   links_        = (Fl_Help_Link *)0;
03063 
03064   atargets_     = 0;
03065   ntargets_     = 0;
03066   targets_      = (Fl_Help_Target *)0;
03067 
03068   directory_[0] = '\0';
03069   filename_[0]  = '\0';
03070 
03071   topline_      = 0;
03072   leftline_     = 0;
03073   size_         = 0;
03074   hsize_        = 0;
03075   scrollbar_size_ = 0;
03076 
03077   scrollbar_.value(0, hh, 0, 1);
03078   scrollbar_.step(8.0);
03079   scrollbar_.show();
03080   scrollbar_.callback(scrollbar_callback);
03081 
03082   hscrollbar_.value(0, ww, 0, 1);
03083   hscrollbar_.step(8.0);
03084   hscrollbar_.show();
03085   hscrollbar_.callback(hscrollbar_callback);
03086   hscrollbar_.type(FL_HORIZONTAL);
03087   end();
03088 
03089   resize(xx, yy, ww, hh);
03090 }
03091 
03092 
03098 Fl_Help_View::~Fl_Help_View()
03099 {
03100   clear_selection();
03101   free_data();
03102 }
03103 
03104 
03109 int                             // O - 0 on success, -1 on error
03110 Fl_Help_View::load(const char *f)// I - Filename to load (may also have target)
03111 {
03112   FILE          *fp;            // File to read from
03113   long          len;            // Length of file
03114   char          *target;        // Target in file
03115   char          *slash;         // Directory separator
03116   const char    *localname;     // Local filename
03117   char          error[1024];    // Error buffer
03118   char          newname[FL_PATH_MAX];   // New filename buffer
03119 
03120   // printf("load(%s)\n",f); fflush(stdout);
03121 
03122   if (strncmp(f, "ftp:", 4) == 0 ||
03123       strncmp(f, "http:", 5) == 0 ||
03124       strncmp(f, "https:", 6) == 0 ||
03125       strncmp(f, "ipp:", 4) == 0 ||
03126       strncmp(f, "mailto:", 7) == 0 ||
03127       strncmp(f, "news:", 5) == 0) {
03128     char urimsg[FL_PATH_MAX];
03129     if ( fl_open_uri(f, urimsg, sizeof(urimsg)) == 0 ) {
03130       clear_selection();
03131 
03132       strlcpy(newname, f, sizeof(newname));
03133       if ((target = strrchr(newname, '#')) != NULL)
03134         *target++ = '\0';
03135 
03136       if (link_)
03137         localname = (*link_)(this, newname);
03138       else
03139         localname = filename_;
03140 
03141       if (!localname)
03142         return (0);
03143 
03144       free_data();
03145 
03146       strlcpy(filename_, newname, sizeof(filename_));
03147       strlcpy(directory_, newname, sizeof(directory_));
03148 
03149       // Note: We do not support Windows backslashes, since they are illegal
03150       //       in URLs...
03151       if ((slash = strrchr(directory_, '/')) == NULL)
03152         directory_[0] = '\0';
03153       else if (slash > directory_ && slash[-1] != '/')
03154         *slash = '\0';
03155 
03156       snprintf(error, sizeof(error),
03157                "<HTML><HEAD><TITLE>Error</TITLE></HEAD>"
03158                "<BODY><H1>Error</H1>"
03159                "<P>Unable to follow the link \"%s\" - "
03160                "%s.</P></BODY>",
03161                f, urimsg);
03162       value(error);
03163       //return(-1);
03164     }
03165     return(0);
03166   }
03167 
03168   clear_selection();
03169 
03170   strlcpy(newname, f, sizeof(newname));
03171   if ((target = strrchr(newname, '#')) != NULL)
03172     *target++ = '\0';
03173 
03174   if (link_)
03175     localname = (*link_)(this, newname);
03176   else
03177     localname = filename_;
03178 
03179   if (!localname)
03180     return (0);
03181 
03182   free_data();
03183 
03184   strlcpy(filename_, newname, sizeof(filename_));
03185   strlcpy(directory_, newname, sizeof(directory_));
03186 
03187   // Note: We do not support Windows backslashes, since they are illegal
03188   //       in URLs...
03189   if ((slash = strrchr(directory_, '/')) == NULL)
03190     directory_[0] = '\0';
03191   else if (slash > directory_ && slash[-1] != '/')
03192     *slash = '\0';
03193 
03194   if (strncmp(localname, "file:", 5) == 0)
03195     localname += 5;     // Adjust for local filename...
03196 
03197   if ((fp = fl_fopen(localname, "rb")) != NULL)
03198   {
03199     fseek(fp, 0, SEEK_END);
03200     len = ftell(fp);
03201     rewind(fp);
03202 
03203     value_ = (const char *)calloc(len + 1, 1);
03204     if (fread((void *)value_, 1, len, fp)==0) { /* use default 0 */ }
03205     fclose(fp);
03206   }
03207   else
03208   {
03209     snprintf(error, sizeof(error),
03210              "<HTML><HEAD><TITLE>Error</TITLE></HEAD>"
03211              "<BODY><H1>Error</H1>"
03212              "<P>Unable to follow the link \"%s\" - "
03213              "%s.</P></BODY>",
03214              localname, strerror(errno));
03215     value_ = strdup(error);
03216   }
03217 
03218   initial_load = 1;
03219   format();
03220   initial_load = 0;
03221 
03222   if (target)
03223     topline(target);
03224   else
03225     topline(0);
03226 
03227   return (0);
03228 }
03229 
03230 
03233 void
03234 Fl_Help_View::resize(int xx,    // I - New left position
03235                      int yy,    // I - New top position
03236                      int ww,    // I - New width
03237                      int hh)    // I - New height
03238 {
03239   Fl_Boxtype            b = box() ? box() : FL_DOWN_BOX;
03240                                         // Box to draw...
03241 
03242 
03243   Fl_Widget::resize(xx, yy, ww, hh);
03244 
03245   int scrollsize = scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size();
03246   scrollbar_.resize(x() + w() - scrollsize - Fl::box_dw(b) + Fl::box_dx(b),
03247                     y() + Fl::box_dy(b), scrollsize, h() - scrollsize - Fl::box_dh(b));
03248   hscrollbar_.resize(x() + Fl::box_dx(b),
03249                      y() + h() - scrollsize - Fl::box_dh(b) + Fl::box_dy(b),
03250                      w() - scrollsize - Fl::box_dw(b), scrollsize);
03251 
03252   format();
03253 }
03254 
03255 
03260 void
03261 Fl_Help_View::topline(const char *n)    // I - Target name
03262 {
03263   Fl_Help_Target key,                   // Target name key
03264                 *target;                // Pointer to matching target
03265 
03266 
03267   if (ntargets_ == 0)
03268     return;
03269 
03270   strlcpy(key.name, n, sizeof(key.name));
03271 
03272   target = (Fl_Help_Target *)bsearch(&key, targets_, ntargets_, sizeof(Fl_Help_Target),
03273                                  (compare_func_t)compare_targets);
03274 
03275   if (target != NULL)
03276     topline(target->y);
03277 }
03278 
03279 
03287 void
03288 Fl_Help_View::topline(int top)  // I - Top line number
03289 {
03290   if (!value_)
03291     return;
03292 
03293   int scrollsize = scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size();
03294   if (size_ < (h() - scrollsize) || top < 0)
03295     top = 0;
03296   else if (top > size_)
03297     top = size_;
03298 
03299   topline_ = top;
03300 
03301   scrollbar_.value(topline_, h() - scrollsize, 0, size_);
03302 
03303   do_callback();
03304 
03305   redraw();
03306 }
03307 
03308 
03316 void
03317 Fl_Help_View::leftline(int left)        // I - Left position
03318 {
03319   if (!value_)
03320     return;
03321 
03322   int scrollsize = scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size();
03323   if (hsize_ < (w() - scrollsize) || left < 0)
03324     left = 0;
03325   else if (left > hsize_)
03326     left = hsize_;
03327 
03328   leftline_ = left;
03329 
03330   hscrollbar_.value(leftline_, w() - scrollsize, 0, hsize_);
03331 
03332   redraw();
03333 }
03334 
03335 
03343 void
03344 Fl_Help_View::value(const char *val)    // I - Text to view
03345 {
03346   clear_selection();
03347   free_data();
03348   set_changed();
03349 
03350   if (!val)
03351     return;
03352 
03353   value_ = strdup(val);
03354 
03355   initial_load = 1;
03356   format();
03357   initial_load = 0;
03358 
03359   topline(0);
03360   leftline(0);
03361 }
03362 
03363 
03364 #ifdef ENC
03365 # undef ENC
03366 #endif
03367 // part b in the table seems to be mac_roman - beku
03368 # define ENC(a, b) a
03369 
03370 
03372 static int                      // O - Code or -1 on error
03373 quote_char(const char *p) {     // I - Quoted string
03374   int   i;                      // Looping var
03375   static struct {
03376     const char  *name;
03377     int         namelen;
03378     int         code;
03379   }     *nameptr,               // Pointer into name array
03380         names[] = {             // Quoting names
03381     { "Aacute;", 7, ENC(193,231) },
03382     { "aacute;", 7, ENC(225,135) },
03383     { "Acirc;",  6, ENC(194,229) },
03384     { "acirc;",  6, ENC(226,137) },
03385     { "acute;",  6, ENC(180,171) },
03386     { "AElig;",  6, ENC(198,174) },
03387     { "aelig;",  6, ENC(230,190) },
03388     { "Agrave;", 7, ENC(192,203) },
03389     { "agrave;", 7, ENC(224,136) },
03390     { "amp;",    4, ENC('&','&') },
03391     { "Aring;",  6, ENC(197,129) },
03392     { "aring;",  6, ENC(229,140) },
03393     { "Atilde;", 7, ENC(195,204) },
03394     { "atilde;", 7, ENC(227,139) },
03395     { "Auml;",   5, ENC(196,128) },
03396     { "auml;",   5, ENC(228,138) },
03397     { "brvbar;", 7, ENC(166, -1) },
03398     { "bull;",   5, ENC(149,165) },
03399     { "Ccedil;", 7, ENC(199,199) },
03400     { "ccedil;", 7, ENC(231,141) },
03401     { "cedil;",  6, ENC(184,252) },
03402     { "cent;",   5, ENC(162,162) },
03403     { "copy;",   5, ENC(169,169) },
03404     { "curren;", 7, ENC(164, -1) },
03405     { "deg;",    4, ENC(176,161) },
03406     { "divide;", 7, ENC(247,214) },
03407     { "Eacute;", 7, ENC(201,131) },
03408     { "eacute;", 7, ENC(233,142) },
03409     { "Ecirc;",  6, ENC(202,230) },
03410     { "ecirc;",  6, ENC(234,144) },
03411     { "Egrave;", 7, ENC(200,233) },
03412     { "egrave;", 7, ENC(232,143) },
03413     { "ETH;",    4, ENC(208, -1) },
03414     { "eth;",    4, ENC(240, -1) },
03415     { "Euml;",   5, ENC(203,232) },
03416     { "euml;",   5, ENC(235,145) },
03417     { "euro;",   5, ENC(128,219) },
03418     { "frac12;", 7, ENC(189, -1) },
03419     { "frac14;", 7, ENC(188, -1) },
03420     { "frac34;", 7, ENC(190, -1) },
03421     { "gt;",     3, ENC('>','>') },
03422     { "Iacute;", 7, ENC(205,234) },
03423     { "iacute;", 7, ENC(237,146) },
03424     { "Icirc;",  6, ENC(206,235) },
03425     { "icirc;",  6, ENC(238,148) },
03426     { "iexcl;",  6, ENC(161,193) },
03427     { "Igrave;", 7, ENC(204,237) },
03428     { "igrave;", 7, ENC(236,147) },
03429     { "iquest;", 7, ENC(191,192) },
03430     { "Iuml;",   5, ENC(207,236) },
03431     { "iuml;",   5, ENC(239,149) },
03432     { "laquo;",  6, ENC(171,199) },
03433     { "lt;",     3, ENC('<','<') },
03434     { "macr;",   5, ENC(175,248) },
03435     { "micro;",  6, ENC(181,181) },
03436     { "middot;", 7, ENC(183,225) },
03437     { "nbsp;",   5, ENC(' ',' ') },
03438     { "not;",    4, ENC(172,194) },
03439     { "Ntilde;", 7, ENC(209,132) },
03440     { "ntilde;", 7, ENC(241,150) },
03441     { "Oacute;", 7, ENC(211,238) },
03442     { "oacute;", 7, ENC(243,151) },
03443     { "Ocirc;",  6, ENC(212,239) },
03444     { "ocirc;",  6, ENC(244,153) },
03445     { "Ograve;", 7, ENC(210,241) },
03446     { "ograve;", 7, ENC(242,152) },
03447     { "ordf;",   5, ENC(170,187) },
03448     { "ordm;",   5, ENC(186,188) },
03449     { "Oslash;", 7, ENC(216,175) },
03450     { "oslash;", 7, ENC(248,191) },
03451     { "Otilde;", 7, ENC(213,205) },
03452     { "otilde;", 7, ENC(245,155) },
03453     { "Ouml;",   5, ENC(214,133) },
03454     { "ouml;",   5, ENC(246,154) },
03455     { "para;",   5, ENC(182,166) },
03456     { "premil;", 7, ENC(137,228) },
03457     { "plusmn;", 7, ENC(177,177) },
03458     { "pound;",  6, ENC(163,163) },
03459     { "quot;",   5, ENC('\"','\"') },
03460     { "raquo;",  6, ENC(187,200) },
03461     { "reg;",    4, ENC(174,168) },
03462     { "sect;",   5, ENC(167,164) },
03463     { "shy;",    4, ENC(173,'-') },
03464     { "sup1;",   5, ENC(185, -1) },
03465     { "sup2;",   5, ENC(178, -1) },
03466     { "sup3;",   5, ENC(179, -1) },
03467     { "szlig;",  6, ENC(223,167) },
03468     { "THORN;",  6, ENC(222, -1) },
03469     { "thorn;",  6, ENC(254, -1) },
03470     { "times;",  6, ENC(215,'x') },
03471     { "trade;",  6, ENC(153,170) },
03472     { "Uacute;", 7, ENC(218,242) },
03473     { "uacute;", 7, ENC(250,156) },
03474     { "Ucirc;",  6, ENC(219,243) },
03475     { "ucirc;",  6, ENC(251,158) },
03476     { "Ugrave;", 7, ENC(217,244) },
03477     { "ugrave;", 7, ENC(249,157) },
03478     { "uml;",    4, ENC(168,172) },
03479     { "Uuml;",   5, ENC(220,134) },
03480     { "uuml;",   5, ENC(252,159) },
03481     { "Yacute;", 7, ENC(221, -1) },
03482     { "yacute;", 7, ENC(253, -1) },
03483     { "yen;",    4, ENC(165,180) },
03484     { "Yuml;",   5, ENC(159,217) },
03485     { "yuml;",   5, ENC(255,216) }
03486   };
03487 
03488   if (!strchr(p, ';')) return -1;
03489   if (*p == '#') {
03490     if (*(p+1) == 'x' || *(p+1) == 'X') return strtol(p+2, NULL, 16);
03491     else return atoi(p+1);
03492   }
03493   for (i = (int)(sizeof(names) / sizeof(names[0])), nameptr = names; i > 0; i --, nameptr ++)
03494     if (strncmp(p, nameptr->name, nameptr->namelen) == 0)
03495       return nameptr->code;
03496 
03497   return -1;
03498 }
03499 
03500 
03502 static void
03503 scrollbar_callback(Fl_Widget *s, void *)
03504 {
03505   ((Fl_Help_View *)(s->parent()))->topline(int(((Fl_Scrollbar*)s)->value()));
03506 }
03507 
03508 
03510 static void
03511 hscrollbar_callback(Fl_Widget *s, void *)
03512 {
03513   ((Fl_Help_View *)(s->parent()))->leftline(int(((Fl_Scrollbar*)s)->value()));
03514 }
03515 
03516 
03517 //
03518 // End of "$Id: Fl_Help_View.cxx 8063 2010-12-19 21:20:10Z matt $".
03519 //