source: rtems-graphics-toolkit/fltk-1.1.10/src/Fl_Help_View.cxx @ 513eea1

Last change on this file since 513eea1 was 513eea1, checked in by Joel Sherrill <joel.sherrill@…>, on 01/09/10 at 22:43:24

2010-01-08 Joel Sherrill <joel.sherrill@…>

fltk 1.1.10. imported

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