| // |
| // "$Id: Fl_Help_View.cxx 8063 2010-12-19 21:20:10Z matt $" |
| // |
| // Fl_Help_View widget routines. |
| // |
| // Copyright 1997-2010 by Easy Software Products. |
| // Image support by Matthias Melcher, Copyright 2000-2009. |
| // |
| // This library is free software; you can redistribute it and/or |
| // modify it under the terms of the GNU Library General Public |
| // License as published by the Free Software Foundation; either |
| // version 2 of the License, or (at your option) any later version. |
| // |
| // This library is distributed in the hope that it will be useful, |
| // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| // Library General Public License for more details. |
| // |
| // You should have received a copy of the GNU Library General Public |
| // License along with this library; if not, write to the Free Software |
| // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
| // USA. |
| // |
| // Please report all bugs and problems on the following page: |
| // |
| // http://www.fltk.org/str.php |
| // |
| // Contents: |
| // |
| // Fl_Help_View::add_block() - Add a text block to the list. |
| // Fl_Help_View::add_link() - Add a new link to the list. |
| // Fl_Help_View::add_target() - Add a new target to the list. |
| // Fl_Help_View::compare_targets() - Compare two targets. |
| // Fl_Help_View::do_align() - Compute the alignment for a line in |
| // a block. |
| // Fl_Help_View::draw() - Draw the Fl_Help_View widget. |
| // Fl_Help_View::format() - Format the help text. |
| // Fl_Help_View::format_table() - Format a table... |
| // Fl_Help_View::free_data() - Free memory used for the document. |
| // Fl_Help_View::get_align() - Get an alignment attribute. |
| // Fl_Help_View::get_attr() - Get an attribute value from the string. |
| // Fl_Help_View::get_color() - Get an alignment attribute. |
| // Fl_Help_View::handle() - Handle events in the widget. |
| // Fl_Help_View::Fl_Help_View() - Build a Fl_Help_View widget. |
| // Fl_Help_View::~Fl_Help_View() - Destroy a Fl_Help_View widget. |
| // Fl_Help_View::load() - Load the specified file. |
| // Fl_Help_View::resize() - Resize the help widget. |
| // Fl_Help_View::topline() - Set the top line to the named target. |
| // Fl_Help_View::topline() - Set the top line by number. |
| // Fl_Help_View::value() - Set the help text directly. |
| // scrollbar_callback() - A callback for the scrollbar. |
| // |
| |
| // |
| // Include necessary header files... |
| // |
| |
| #include <FL/Fl_Help_View.H> |
| #include <FL/Fl_Window.H> |
| #include <FL/Fl_Pixmap.H> |
| #include <FL/x.H> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <FL/fl_utf8.h> |
| #include <FL/filename.H> // fl_open_uri() |
| #include "flstring.h" |
| #include <ctype.h> |
| #include <errno.h> |
| #include <math.h> |
| |
| #if defined(WIN32) && ! defined(__CYGWIN__) |
| # include <io.h> |
| # include <direct.h> |
| #else |
| # include <unistd.h> |
| #endif // WIN32 |
| |
| #define MAX_COLUMNS 200 |
| |
| |
| // |
| // Typedef the C API sort function type the only way I know how... |
| // |
| |
| extern "C" |
| { |
| typedef int (*compare_func_t)(const void *, const void *); |
| } |
| |
| |
| // |
| // Local functions... |
| // |
| |
| static int quote_char(const char *); |
| static void scrollbar_callback(Fl_Widget *s, void *); |
| static void hscrollbar_callback(Fl_Widget *s, void *); |
| |
| // |
| // global flag for image loading (see get_image). |
| // |
| |
| static char initial_load = 0; |
| |
| // |
| // Broken image... |
| // |
| |
| static const char *broken_xpm[] = |
| { |
| "16 24 4 1", |
| "@ c #000000", |
| " c #ffffff", |
| "+ c none", |
| "x c #ff0000", |
| // pixels |
| "@@@@@@@+++++++++", |
| "@ @++++++++++", |
| "@ @+++++++++++", |
| "@ @++@++++++++", |
| "@ @@+++++++++", |
| "@ @+++@+++++", |
| "@ @++@@++++@", |
| "@ xxx @@ @++@@", |
| "@ xxx xx@@ @", |
| "@ xxx xxx @", |
| "@ xxxxxx @", |
| "@ xxxx @", |
| "@ xxxxxx @", |
| "@ xxx xxx @", |
| "@ xxx xxx @", |
| "@ xxx xxx @", |
| "@ @", |
| "@ @", |
| "@ @", |
| "@ @", |
| "@ @", |
| "@ @", |
| "@ @", |
| "@@@@@@@@@@@@@@@@", |
| NULL |
| }; |
| |
| static Fl_Pixmap broken_image(broken_xpm); |
| |
| // |
| // Simple margin stack for Fl_Help_View::format()... |
| // |
| |
| struct fl_margins { |
| int depth_; |
| int margins_[100]; |
| |
| fl_margins() { clear(); } |
| |
| int clear() { |
| // puts("fl_margins::clear()"); |
| |
| depth_ = 0; |
| return margins_[0] = 4; |
| } |
| |
| int current() { return margins_[depth_]; } |
| |
| int pop() { |
| // printf("fl_margins::pop(): depth_=%d, xx=%d\n", depth_, |
| // depth_ > 0 ? margins_[depth_ - 1] : 4); |
| |
| if (depth_ > 0) { |
| depth_ --; |
| return margins_[depth_]; |
| } else return 4; |
| } |
| |
| int push(int indent) { |
| int xx; |
| |
| xx = margins_[depth_] + indent; |
| |
| // printf("fl_margins::push(indent=%d): depth_=%d, xx=%d\n", indent, |
| // depth_ + 1, xx); |
| |
| if (depth_ < 99) { |
| depth_ ++; |
| margins_[depth_] = xx; |
| } |
| |
| return xx; |
| } |
| }; |
| |
| // |
| // All the stuff needed to implement text selection in Fl_Help_View |
| // |
| |
| /* matt: |
| * We are trying to keep binary compatibility with previous versions |
| * of FLTK. This means that we are limited to adding static variables |
| * only to not enlarge the Fl_Help_View class. Lucky for us, only one |
| * text can be selected system wide, so we can remember the selection |
| * in a single set of variables. |
| * |
| * Still to do: |
| * - &word; style characters mess up our count inside a word boundary |
| * - we can only select words, no individual characters |
| * - no dragging of the selection into another widget |
| * - selection must be cleared if another widget get focus! |
| * - write a comment for every new function |
| */ |
| |
| /* |
| The following functions are also used to draw stuff and should be replaced with |
| local copies that are much faster when merely counting: |
| |
| fl_color(Fl_Color); |
| fl_rectf(int, int, int, int); |
| fl_push_clip(int, int, int, int); |
| fl_xyline(int, int, int); |
| fl_rect() |
| fl_line() |
| img->draw() |
| */ |
| |
| // We don't put the offscreen buffer in the help view class because |
| // we'd need to include x.H in the header... |
| static Fl_Offscreen fl_help_view_buffer; |
| int Fl_Help_View::selection_first = 0; |
| int Fl_Help_View::selection_last = 0; |
| int Fl_Help_View::selection_push_first = 0; |
| int Fl_Help_View::selection_push_last = 0; |
| int Fl_Help_View::selection_drag_first = 0; |
| int Fl_Help_View::selection_drag_last = 0; |
| int Fl_Help_View::selected = 0; |
| int Fl_Help_View::draw_mode = 0; |
| int Fl_Help_View::mouse_x = 0; |
| int Fl_Help_View::mouse_y = 0; |
| int Fl_Help_View::current_pos = 0; |
| Fl_Help_View *Fl_Help_View::current_view = 0L; |
| Fl_Color Fl_Help_View::hv_selection_color; |
| Fl_Color Fl_Help_View::hv_selection_text_color; |
| |
| /* |
| * Limitation: if a word contains &code; notations, we will calculate a wrong length. |
| * |
| * This function must be optimized for speed! |
| */ |
| void Fl_Help_View::hv_draw(const char *t, int x, int y) |
| { |
| if (selected && current_view==this && current_pos<selection_last && current_pos>=selection_first) { |
| Fl_Color c = fl_color(); |
| fl_color(hv_selection_color); |
| int w = (int)fl_width(t); |
| if (current_pos+(int)strlen(t)<selection_last) |
| w += (int)fl_width(' '); |
| fl_rectf(x, y+fl_descent()-fl_height(), w, fl_height()); |
| fl_color(hv_selection_text_color); |
| fl_draw(t, x, y); |
| fl_color(c); |
| } else { |
| fl_draw(t, x, y); |
| } |
| if (draw_mode) { |
| int w = (int)fl_width(t); |
| if (mouse_x>=x && mouse_x<x+w) { |
| if (mouse_y>=y-fl_height()+fl_descent()&&mouse_y<=y+fl_descent()) { |
| int f = current_pos; |
| int l = f+strlen(t); // use 'quote_char' to calculate the true length of the HTML string |
| if (draw_mode==1) { |
| selection_push_first = f; |
| selection_push_last = l; |
| } else { |
| selection_drag_first = f; |
| selection_drag_last = l; |
| } |
| } |
| } |
| } |
| } |
| |
| |
| /** Adds a text block to the list. */ |
| Fl_Help_Block * // O - Pointer to new block |
| Fl_Help_View::add_block(const char *s, // I - Pointer to start of block text |
| int xx, // I - X position of block |
| int yy, // I - Y position of block |
| int ww, // I - Right margin of block |
| int hh, // I - Height of block |
| unsigned char border) // I - Draw border? |
| { |
| Fl_Help_Block *temp; // New block |
| |
| |
| // printf("add_block(s = %p, xx = %d, yy = %d, ww = %d, hh = %d, border = %d)\n", |
| // s, xx, yy, ww, hh, border); |
| |
| if (nblocks_ >= ablocks_) |
| { |
| ablocks_ += 16; |
| |
| if (ablocks_ == 16) |
| blocks_ = (Fl_Help_Block *)malloc(sizeof(Fl_Help_Block) * ablocks_); |
| else |
| blocks_ = (Fl_Help_Block *)realloc(blocks_, sizeof(Fl_Help_Block) * ablocks_); |
| } |
| |
| temp = blocks_ + nblocks_; |
| memset(temp, 0, sizeof(Fl_Help_Block)); |
| temp->start = s; |
| temp->end = s; |
| temp->x = xx; |
| temp->y = yy; |
| temp->w = ww; |
| temp->h = hh; |
| temp->border = border; |
| temp->bgcolor = bgcolor_; |
| nblocks_ ++; |
| |
| return (temp); |
| } |
| |
| |
| /** Adds a new link to the list. */ |
| void Fl_Help_View::add_link(const char *n, // I - Name of link |
| int xx, // I - X position of link |
| int yy, // I - Y position of link |
| int ww, // I - Width of link text |
| int hh) // I - Height of link text |
| { |
| Fl_Help_Link *temp; // New link |
| char *target; // Pointer to target name |
| |
| |
| if (nlinks_ >= alinks_) |
| { |
| alinks_ += 16; |
| |
| if (alinks_ == 16) |
| links_ = (Fl_Help_Link *)malloc(sizeof(Fl_Help_Link) * alinks_); |
| else |
| links_ = (Fl_Help_Link *)realloc(links_, sizeof(Fl_Help_Link) * alinks_); |
| } |
| |
| temp = links_ + nlinks_; |
| |
| temp->x = xx; |
| temp->y = yy; |
| temp->w = xx + ww; |
| temp->h = yy + hh; |
| |
| strlcpy(temp->filename, n, sizeof(temp->filename)); |
| |
| if ((target = strrchr(temp->filename, '#')) != NULL) |
| { |
| *target++ = '\0'; |
| strlcpy(temp->name, target, sizeof(temp->name)); |
| } |
| else |
| temp->name[0] = '\0'; |
| |
| nlinks_ ++; |
| } |
| |
| |
| /** Adds a new target to the list. */ |
| void Fl_Help_View::add_target(const char *n, // I - Name of target |
| int yy) // I - Y position of target |
| { |
| Fl_Help_Target *temp; // New target |
| |
| |
| if (ntargets_ >= atargets_) |
| { |
| atargets_ += 16; |
| |
| if (atargets_ == 16) |
| targets_ = (Fl_Help_Target *)malloc(sizeof(Fl_Help_Target) * atargets_); |
| else |
| targets_ = (Fl_Help_Target *)realloc(targets_, sizeof(Fl_Help_Target) * atargets_); |
| } |
| |
| temp = targets_ + ntargets_; |
| |
| temp->y = yy; |
| strlcpy(temp->name, n, sizeof(temp->name)); |
| |
| ntargets_ ++; |
| } |
| |
| /** Compares two targets.*/ |
| int // O - Result of comparison |
| Fl_Help_View::compare_targets(const Fl_Help_Target *t0, // I - First target |
| const Fl_Help_Target *t1) // I - Second target |
| { |
| return (strcasecmp(t0->name, t1->name)); |
| } |
| |
| /** Computes the alignment for a line in a block.*/ |
| int // O - New line |
| Fl_Help_View::do_align(Fl_Help_Block *block, // I - Block to add to |
| int line, // I - Current line |
| int xx, // I - Current X position |
| int a, // I - Current alignment |
| int &l) // IO - Starting link |
| { |
| int offset; // Alignment offset |
| |
| |
| switch (a) |
| { |
| case RIGHT : // Right align |
| offset = block->w - xx; |
| break; |
| case CENTER : // Center |
| offset = (block->w - xx) / 2; |
| break; |
| default : // Left align |
| offset = 0; |
| break; |
| } |
| |
| block->line[line] = block->x + offset; |
| |
| if (line < 31) |
| line ++; |
| |
| while (l < nlinks_) |
| { |
| links_[l].x += offset; |
| links_[l].w += offset; |
| l ++; |
| } |
| |
| return (line); |
| } |
| |
| /** Draws the Fl_Help_View widget. */ |
| void |
| Fl_Help_View::draw() |
| { |
| int i; // Looping var |
| const Fl_Help_Block *block; // Pointer to current block |
| const char *ptr, // Pointer to text in block |
| *attrs; // Pointer to start of element attributes |
| char *s, // Pointer into buffer |
| buf[1024], // Text buffer |
| attr[1024]; // Attribute buffer |
| int xx, yy, ww, hh; // Current positions and sizes |
| int line; // Current line |
| Fl_Font font; |
| Fl_Fontsize fsize; // Current font and size |
| Fl_Color fcolor; // current font color |
| int head, pre, // Flags for text |
| needspace; // Do we need whitespace? |
| Fl_Boxtype b = box() ? box() : FL_DOWN_BOX; |
| // Box to draw... |
| int underline, // Underline text? |
| xtra_ww; // Extra width for underlined space between words |
| |
| // Draw the scrollbar(s) and box first... |
| ww = w(); |
| hh = h(); |
| i = 0; |
| |
| draw_box(b, x(), y(), ww, hh, bgcolor_); |
| |
| if ( hscrollbar_.visible() || scrollbar_.visible() ) { |
| int scrollsize = scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size(); |
| int hor_vis = hscrollbar_.visible(); |
| int ver_vis = scrollbar_.visible(); |
| // Scrollbar corner |
| int scorn_x = x() + ww - (ver_vis?scrollsize:0) - Fl::box_dw(b) + Fl::box_dx(b); |
| int scorn_y = y() + hh - (hor_vis?scrollsize:0) - Fl::box_dh(b) + Fl::box_dy(b); |
| if ( hor_vis ) { |
| if ( hscrollbar_.h() != scrollsize ) { // scrollsize changed? |
| hscrollbar_.resize(x(), scorn_y, scorn_x - x(), scrollsize); |
| init_sizes(); |
| } |
| draw_child(hscrollbar_); |
| hh -= scrollsize; |
| } |
| if ( ver_vis ) { |
| if ( scrollbar_.w() != scrollsize ) { // scrollsize changed? |
| scrollbar_.resize(scorn_x, y(), scrollsize, scorn_y - y()); |
| init_sizes(); |
| } |
| draw_child(scrollbar_); |
| ww -= scrollsize; |
| } |
| if ( hor_vis && ver_vis ) { |
| // Both scrollbars visible? Draw little gray box in corner |
| fl_color(FL_GRAY); |
| fl_rectf(scorn_x, scorn_y, scrollsize, scrollsize); |
| } |
| } |
| |
| if (!value_) |
| return; |
| |
| if (current_view == this && selected) { |
| hv_selection_color = FL_SELECTION_COLOR; |
| hv_selection_text_color = fl_contrast(textcolor_, FL_SELECTION_COLOR); |
| } |
| current_pos = 0; |
| |
| // Clip the drawing to the inside of the box... |
| fl_push_clip(x() + Fl::box_dx(b), y() + Fl::box_dy(b), |
| ww - Fl::box_dw(b), hh - Fl::box_dh(b)); |
| fl_color(textcolor_); |
| |
| // Draw all visible blocks... |
| for (i = 0, block = blocks_; i < nblocks_; i ++, block ++) |
| if ((block->y + block->h) >= topline_ && block->y < (topline_ + h())) |
| { |
| line = 0; |
| xx = block->line[line]; |
| yy = block->y - topline_; |
| hh = 0; |
| pre = 0; |
| head = 0; |
| needspace = 0; |
| underline = 0; |
| |
| initfont(font, fsize, fcolor); |
| |
| for (ptr = block->start, s = buf; ptr < block->end;) |
| { |
| if ((*ptr == '<' || isspace((*ptr)&255)) && s > buf) |
| { |
| if (!head && !pre) |
| { |
| // Check width... |
| *s = '\0'; |
| s = buf; |
| ww = (int)fl_width(buf); |
| |
| if (needspace && xx > block->x) |
| xx += (int)fl_width(' '); |
| |
| if ((xx + ww) > block->w) |
| { |
| if (line < 31) |
| line ++; |
| xx = block->line[line]; |
| yy += hh; |
| hh = 0; |
| } |
| |
| hv_draw(buf, xx + x() - leftline_, yy + y()); |
| if (underline) { |
| xtra_ww = isspace((*ptr)&255)?(int)fl_width(' '):0; |
| fl_xyline(xx + x() - leftline_, yy + y() + 1, |
| xx + x() - leftline_ + ww + xtra_ww); |
| } |
| current_pos = ptr-value_; |
| |
| xx += ww; |
| if ((fsize + 2) > hh) |
| hh = fsize + 2; |
| |
| needspace = 0; |
| } |
| else if (pre) |
| { |
| while (isspace((*ptr)&255)) |
| { |
| if (*ptr == '\n') |
| { |
| *s = '\0'; |
| s = buf; |
| |
| hv_draw(buf, xx + x() - leftline_, yy + y()); |
| if (underline) fl_xyline(xx + x() - leftline_, yy + y() + 1, |
| xx + x() - leftline_ + |
| (int)fl_width(buf)); |
| |
| current_pos = ptr-value_; |
| if (line < 31) |
| line ++; |
| xx = block->line[line]; |
| yy += hh; |
| hh = fsize + 2; |
| } |
| else if (*ptr == '\t') |
| { |
| // Do tabs every 8 columns... |
| while (((s - buf) & 7)) |
| *s++ = ' '; |
| } |
| else |
| *s++ = ' '; |
| |
| if ((fsize + 2) > hh) |
| hh = fsize + 2; |
| |
| ptr ++; |
| } |
| |
| if (s > buf) |
| { |
| *s = '\0'; |
| s = buf; |
| |
| hv_draw(buf, xx + x() - leftline_, yy + y()); |
| ww = (int)fl_width(buf); |
| if (underline) fl_xyline(xx + x() - leftline_, yy + y() + 1, |
| xx + x() - leftline_ + ww); |
| xx += ww; |
| current_pos = ptr-value_; |
| } |
| |
| needspace = 0; |
| } |
| else |
| { |
| s = buf; |
| |
| while (isspace((*ptr)&255)) |
| ptr ++; |
| current_pos = ptr-value_; |
| } |
| } |
| |
| if (*ptr == '<') |
| { |
| ptr ++; |
| |
| if (strncmp(ptr, "!--", 3) == 0) |
| { |
| // Comment... |
| ptr += 3; |
| if ((ptr = strstr(ptr, "-->")) != NULL) |
| { |
| ptr += 3; |
| continue; |
| } |
| else |
| break; |
| } |
| |
| while (*ptr && *ptr != '>' && !isspace((*ptr)&255)) |
| if (s < (buf + sizeof(buf) - 1)) |
| *s++ = *ptr++; |
| else |
| ptr ++; |
| |
| *s = '\0'; |
| s = buf; |
| |
| attrs = ptr; |
| while (*ptr && *ptr != '>') |
| ptr ++; |
| |
| if (*ptr == '>') |
| ptr ++; |
| |
| // end of command reached, set the supposed start of printed eord here |
| current_pos = ptr-value_; |
| if (strcasecmp(buf, "HEAD") == 0) |
| head = 1; |
| else if (strcasecmp(buf, "BR") == 0) |
| { |
| if (line < 31) |
| line ++; |
| xx = block->line[line]; |
| yy += hh; |
| hh = 0; |
| } |
| else if (strcasecmp(buf, "HR") == 0) |
| { |
| fl_line(block->x + x(), yy + y(), block->w + x(), |
| yy + y()); |
| |
| if (line < 31) |
| line ++; |
| xx = block->line[line]; |
| yy += 2 * hh; |
| hh = 0; |
| } |
| else if (strcasecmp(buf, "CENTER") == 0 || |
| strcasecmp(buf, "P") == 0 || |
| strcasecmp(buf, "H1") == 0 || |
| strcasecmp(buf, "H2") == 0 || |
| strcasecmp(buf, "H3") == 0 || |
| strcasecmp(buf, "H4") == 0 || |
| strcasecmp(buf, "H5") == 0 || |
| strcasecmp(buf, "H6") == 0 || |
| strcasecmp(buf, "UL") == 0 || |
| strcasecmp(buf, "OL") == 0 || |
| strcasecmp(buf, "DL") == 0 || |
| strcasecmp(buf, "LI") == 0 || |
| strcasecmp(buf, "DD") == 0 || |
| strcasecmp(buf, "DT") == 0 || |
| strcasecmp(buf, "PRE") == 0) |
| { |
| if (tolower(buf[0]) == 'h') |
| { |
| font = FL_HELVETICA_BOLD; |
| fsize = textsize_ + '7' - buf[1]; |
| } |
| else if (strcasecmp(buf, "DT") == 0) |
| { |
| font = textfont_ | FL_ITALIC; |
| fsize = textsize_; |
| } |
| else if (strcasecmp(buf, "PRE") == 0) |
| { |
| font = FL_COURIER; |
| fsize = textsize_; |
| pre = 1; |
| } |
| |
| if (strcasecmp(buf, "LI") == 0) |
| { |
| // fl_font(FL_SYMBOL, fsize); // The default SYMBOL font on my XP box is not Unicode... |
| char buf[8]; |
| wchar_t b[] = {0x2022, 0x0}; |
| // buf[fl_unicode2utf(b, 1, buf)] = 0; |
| unsigned dstlen = fl_utf8fromwc(buf, 8, b, 1); |
| buf[dstlen] = 0; |
| hv_draw(buf, xx - fsize + x() - leftline_, yy + y()); |
| } |
| |
| pushfont(font, fsize); |
| } |
| else if (strcasecmp(buf, "A") == 0 && |
| get_attr(attrs, "HREF", attr, sizeof(attr)) != NULL) |
| { |
| fl_color(linkcolor_); |
| underline = 1; |
| } |
| else if (strcasecmp(buf, "/A") == 0) |
| { |
| fl_color(textcolor_); |
| underline = 0; |
| } |
| else if (strcasecmp(buf, "FONT") == 0) |
| { |
| if (get_attr(attrs, "COLOR", attr, sizeof(attr)) != NULL) { |
| textcolor_ = get_color(attr, textcolor_); |
| } |
| |
| if (get_attr(attrs, "FACE", attr, sizeof(attr)) != NULL) { |
| if (!strncasecmp(attr, "helvetica", 9) || |
| !strncasecmp(attr, "arial", 5) || |
| !strncasecmp(attr, "sans", 4)) font = FL_HELVETICA; |
| else if (!strncasecmp(attr, "times", 5) || |
| !strncasecmp(attr, "serif", 5)) font = FL_TIMES; |
| else if (!strncasecmp(attr, "symbol", 6)) font = FL_SYMBOL; |
| else font = FL_COURIER; |
| } |
| |
| if (get_attr(attrs, "SIZE", attr, sizeof(attr)) != NULL) { |
| if (isdigit(attr[0] & 255)) { |
| // Absolute size |
| fsize = (int)(textsize_ * pow(1.2, atof(attr) - 3.0)); |
| } else { |
| // Relative size |
| fsize = (int)(fsize * pow(1.2, atof(attr) - 3.0)); |
| } |
| } |
| |
| pushfont(font, fsize); |
| } |
| else if (strcasecmp(buf, "/FONT") == 0) |
| { |
| popfont(font, fsize, textcolor_); |
| } |
| else if (strcasecmp(buf, "U") == 0) |
| underline = 1; |
| else if (strcasecmp(buf, "/U") == 0) |
| underline = 0; |
| else if (strcasecmp(buf, "B") == 0 || |
| strcasecmp(buf, "STRONG") == 0) |
| pushfont(font |= FL_BOLD, fsize); |
| else if (strcasecmp(buf, "TD") == 0 || |
| strcasecmp(buf, "TH") == 0) |
| { |
| int tx, ty, tw, th; |
| |
| if (tolower(buf[1]) == 'h') |
| pushfont(font |= FL_BOLD, fsize); |
| else |
| pushfont(font = textfont_, fsize); |
| |
| tx = block->x - 4 - leftline_; |
| ty = block->y - topline_ - fsize - 3; |
| tw = block->w - block->x + 7; |
| th = block->h + fsize - 5; |
| |
| if (tx < 0) |
| { |
| tw += tx; |
| tx = 0; |
| } |
| |
| if (ty < 0) |
| { |
| th += ty; |
| ty = 0; |
| } |
| |
| tx += x(); |
| ty += y(); |
| |
| if (block->bgcolor != bgcolor_) |
| { |
| fl_color(block->bgcolor); |
| fl_rectf(tx, ty, tw, th); |
| fl_color(textcolor_); |
| } |
| |
| if (block->border) |
| fl_rect(tx, ty, tw, th); |
| } |
| else if (strcasecmp(buf, "I") == 0 || |
| strcasecmp(buf, "EM") == 0) |
| pushfont(font |= FL_ITALIC, fsize); |
| else if (strcasecmp(buf, "CODE") == 0 || |
| strcasecmp(buf, "TT") == 0) |
| pushfont(font = FL_COURIER, fsize); |
| else if (strcasecmp(buf, "KBD") == 0) |
| pushfont(font = FL_COURIER_BOLD, fsize); |
| else if (strcasecmp(buf, "VAR") == 0) |
| pushfont(font = FL_COURIER_ITALIC, fsize); |
| else if (strcasecmp(buf, "/HEAD") == 0) |
| head = 0; |
| else if (strcasecmp(buf, "/H1") == 0 || |
| strcasecmp(buf, "/H2") == 0 || |
| strcasecmp(buf, "/H3") == 0 || |
| strcasecmp(buf, "/H4") == 0 || |
| strcasecmp(buf, "/H5") == 0 || |
| strcasecmp(buf, "/H6") == 0 || |
| strcasecmp(buf, "/B") == 0 || |
| strcasecmp(buf, "/STRONG") == 0 || |
| strcasecmp(buf, "/I") == 0 || |
| strcasecmp(buf, "/EM") == 0 || |
| strcasecmp(buf, "/CODE") == 0 || |
| strcasecmp(buf, "/TT") == 0 || |
| strcasecmp(buf, "/KBD") == 0 || |
| strcasecmp(buf, "/VAR") == 0) |
| popfont(font, fsize, fcolor); |
| else if (strcasecmp(buf, "/PRE") == 0) |
| { |
| popfont(font, fsize, fcolor); |
| pre = 0; |
| } |
| else if (strcasecmp(buf, "IMG") == 0) |
| { |
| Fl_Shared_Image *img = 0; |
| int width, height; |
| char wattr[8], hattr[8]; |
| |
| |
| get_attr(attrs, "WIDTH", wattr, sizeof(wattr)); |
| get_attr(attrs, "HEIGHT", hattr, sizeof(hattr)); |
| width = get_length(wattr); |
| height = get_length(hattr); |
| |
| if (get_attr(attrs, "SRC", attr, sizeof(attr))) { |
| img = get_image(attr, width, height); |
| if (!width) width = img->w(); |
| if (!height) height = img->h(); |
| } |
| |
| if (!width || !height) { |
| if (get_attr(attrs, "ALT", attr, sizeof(attr)) == NULL) { |
| strcpy(attr, "IMG"); |
| } |
| } |
| |
| ww = width; |
| |
| if (needspace && xx > block->x) |
| xx += (int)fl_width(' '); |
| |
| if ((xx + ww) > block->w) |
| { |
| if (line < 31) |
| line ++; |
| |
| xx = block->line[line]; |
| yy += hh; |
| hh = 0; |
| } |
| |
| if (img) { |
| img->draw(xx + x() - leftline_, |
| yy + y() - fl_height() + fl_descent() + 2); |
| } |
| |
| xx += ww; |
| if ((height + 2) > hh) |
| hh = height + 2; |
| |
| needspace = 0; |
| } |
| } |
| else if (*ptr == '\n' && pre) |
| { |
| *s = '\0'; |
| s = buf; |
| |
| hv_draw(buf, xx + x() - leftline_, yy + y()); |
| |
| if (line < 31) |
| line ++; |
| xx = block->line[line]; |
| yy += hh; |
| hh = fsize + 2; |
| needspace = 0; |
| |
| ptr ++; |
| current_pos = ptr-value_; |
| } |
| else if (isspace((*ptr)&255)) |
| { |
| if (pre) |
| { |
| if (*ptr == ' ') |
| *s++ = ' '; |
| else |
| { |
| // Do tabs every 8 columns... |
| while (((s - buf) & 7)) |
| *s++ = ' '; |
| } |
| } |
| |
| ptr ++; |
| if (!pre) current_pos = ptr-value_; |
| needspace = 1; |
| } |
| else if (*ptr == '&') |
| { |
| ptr ++; |
| |
| int qch = quote_char(ptr); |
| |
| if (qch < 0) |
| *s++ = '&'; |
| else { |
| int l; |
| l = fl_utf8encode((unsigned int) qch, s); |
| if (l < 1) l = 1; |
| s += l; |
| ptr = strchr(ptr, ';') + 1; |
| } |
| |
| if ((fsize + 2) > hh) |
| hh = fsize + 2; |
| } |
| else |
| { |
| *s++ = *ptr++; |
| |
| if ((fsize + 2) > hh) |
| hh = fsize + 2; |
| } |
| } |
| |
| *s = '\0'; |
| |
| if (s > buf && !pre && !head) |
| { |
| ww = (int)fl_width(buf); |
| |
| if (needspace && xx > block->x) |
| xx += (int)fl_width(' '); |
| |
| if ((xx + ww) > block->w) |
| { |
| if (line < 31) |
| line ++; |
| xx = block->line[line]; |
| yy += hh; |
| hh = 0; |
| } |
| } |
| |
| if (s > buf && !head) |
| { |
| hv_draw(buf, xx + x() - leftline_, yy + y()); |
| if (underline) fl_xyline(xx + x() - leftline_, yy + y() + 1, |
| xx + x() - leftline_ + ww); |
| current_pos = ptr-value_; |
| } |
| } |
| |
| fl_pop_clip(); |
| } |
| |
| |
| |
| /** Finds the specified string \p s at starting position \p p. |
| |
| \return the matching position or -1 if not found |
| */ |
| int // O - Matching position or -1 if not found |
| Fl_Help_View::find(const char *s, // I - String to find |
| int p) // I - Starting position |
| { |
| int i, // Looping var |
| c; // Current character |
| Fl_Help_Block *b; // Current block |
| const char *bp, // Block matching pointer |
| *bs, // Start of current comparison |
| *sp; // Search string pointer |
| |
| |
| // Range check input and value... |
| if (!s || !value_) return -1; |
| |
| if (p < 0 || p >= (int)strlen(value_)) p = 0; |
| else if (p > 0) p ++; |
| |
| // Look for the string... |
| for (i = nblocks_, b = blocks_; i > 0; i --, b ++) { |
| if (b->end < (value_ + p)) |
| continue; |
| |
| if (b->start < (value_ + p)) bp = value_ + p; |
| else bp = b->start; |
| |
| for (sp = s, bs = bp; *sp && *bp && bp < b->end; bp ++) { |
| if (*bp == '<') { |
| // skip to end of element... |
| while (*bp && bp < b->end && *bp != '>') bp ++; |
| continue; |
| } else if (*bp == '&') { |
| // decode HTML entity... |
| if ((c = quote_char(bp + 1)) < 0) c = '&'; |
| else bp = strchr(bp + 1, ';') + 1; |
| } else c = *bp; |
| |
| if (tolower(*sp) == tolower(c)) sp ++; |
| else { |
| // No match, so reset to start of search... |
| sp = s; |
| bs ++; |
| bp = bs; |
| } |
| } |
| |
| if (!*sp) { |
| // Found a match! |
| topline(b->y - b->h); |
| return (b->end - value_); |
| } |
| } |
| |
| // No match! |
| return (-1); |
| } |
| |
| /** Formats the help text. */ |
| void Fl_Help_View::format() { |
| int i; // Looping var |
| int done; // Are we done yet? |
| Fl_Help_Block *block, // Current block |
| *cell; // Current table cell |
| int cells[MAX_COLUMNS], |
| // Cells in the current row... |
| row; // Current table row (block number) |
| const char *ptr, // Pointer into block |
| *start, // Pointer to start of element |
| *attrs; // Pointer to start of element attributes |
| char *s, // Pointer into buffer |
| buf[1024], // Text buffer |
| attr[1024], // Attribute buffer |
| wattr[1024], // Width attribute buffer |
| hattr[1024], // Height attribute buffer |
| linkdest[1024]; // Link destination |
| int xx, yy, ww, hh; // Size of current text fragment |
| int line; // Current line in block |
| int links; // Links for current line |
| Fl_Font font; |
| Fl_Fontsize fsize; // Current font and size |
| Fl_Color fcolor; // Current font color |
| unsigned char border; // Draw border? |
| int talign, // Current alignment |
| newalign, // New alignment |
| head, // In the <HEAD> section? |
| pre, // <PRE> text? |
| needspace; // Do we need whitespace? |
| int table_width, // Width of table |
| table_offset; // Offset of table |
| int column, // Current table column number |
| columns[MAX_COLUMNS]; |
| // Column widths |
| Fl_Color tc, rc; // Table/row background color |
| Fl_Boxtype b = box() ? box() : FL_DOWN_BOX; |
| // Box to draw... |
| fl_margins margins; // Left margin stack... |
| |
| |
| // Reset document width... |
| int scrollsize = scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size(); |
| hsize_ = w() - scrollsize - Fl::box_dw(b); |
| |
| done = 0; |
| while (!done) |
| { |
| // Reset state variables... |
| done = 1; |
| nblocks_ = 0; |
| nlinks_ = 0; |
| ntargets_ = 0; |
| size_ = 0; |
| bgcolor_ = color(); |
| textcolor_ = textcolor(); |
| linkcolor_ = fl_contrast(FL_BLUE, color()); |
| |
| tc = rc = bgcolor_; |
| |
| strcpy(title_, "Untitled"); |
| |
| if (!value_) |
| return; |
| |
| // Setup for formatting... |
| initfont(font, fsize, fcolor); |
| |
| line = 0; |
| links = 0; |
| xx = margins.clear(); |
| yy = fsize + 2; |
| ww = 0; |
| column = 0; |
| border = 0; |
| hh = 0; |
| block = add_block(value_, xx, yy, hsize_, 0); |
| row = 0; |
| head = 0; |
| pre = 0; |
| talign = LEFT; |
| newalign = LEFT; |
| needspace = 0; |
| linkdest[0] = '\0'; |
| table_offset = 0; |
| |
| // Html text character loop |
| for (ptr = value_, s = buf; *ptr;) |
| { |
| // End of word? |
| if ((*ptr == '<' || isspace((*ptr)&255)) && s > buf) |
| { |
| // Get width of word parsed so far... |
| *s = '\0'; |
| ww = (int)fl_width(buf); |
| |
| if (!head && !pre) |
| { |
| // Check width... |
| if (ww > hsize_) { |
| hsize_ = ww; |
| done = 0; |
| break; |
| } |
| |
| if (needspace && xx > block->x) |
| ww += (int)fl_width(' '); |
| |
| // printf("line = %d, xx = %d, ww = %d, block->x = %d, block->w = %d\n", |
| // line, xx, ww, block->x, block->w); |
| |
| if ((xx + ww) > block->w) |
| { |
| line = do_align(block, line, xx, newalign, links); |
| xx = block->x; |
| yy += hh; |
| block->h += hh; |
| hh = 0; |
| } |
| |
| if (linkdest[0]) |
| add_link(linkdest, xx, yy - fsize, ww, fsize); |
| |
| xx += ww; |
| if ((fsize + 2) > hh) |
| hh = fsize + 2; |
| |
| needspace = 0; |
| } |
| else if (pre) |
| { |
| // Add a link as needed... |
| if (linkdest[0]) |
| add_link(linkdest, xx, yy - hh, ww, hh); |
| |
| xx += ww; |
| if ((fsize + 2) > hh) |
| hh = fsize + 2; |
| |
| // Handle preformatted text... |
| while (isspace((*ptr)&255)) |
| { |
| if (*ptr == '\n') |
| { |
| if (xx > hsize_) break; |
| |
| line = do_align(block, line, xx, newalign, links); |
| xx = block->x; |
| yy += hh; |
| block->h += hh; |
| hh = fsize + 2; |
| } |
| else |
| xx += (int)fl_width(' '); |
| |
| if ((fsize + 2) > hh) |
| hh = fsize + 2; |
| |
| ptr ++; |
| } |
| |
| if (xx > hsize_) { |
| hsize_ = xx; |
| done = 0; |
| break; |
| } |
| |
| needspace = 0; |
| } |
| else |
| { |
| // Handle normal text or stuff in the <HEAD> section... |
| while (isspace((*ptr)&255)) |
| ptr ++; |
| } |
| |
| s = buf; |
| } |
| |
| if (*ptr == '<') |
| { |
| // Handle html tags.. |
| start = ptr; |
| ptr ++; |
| |
| if (strncmp(ptr, "!--", 3) == 0) |
| { |
| // Comment... |
| ptr += 3; |
| if ((ptr = strstr(ptr, "-->")) != NULL) |
| { |
| ptr += 3; |
| continue; |
| } |
| else |
| break; |
| } |
| |
| while (*ptr && *ptr != '>' && !isspace((*ptr)&255)) |
| if (s < (buf + sizeof(buf) - 1)) |
| *s++ = *ptr++; |
| else |
| ptr ++; |
| |
| *s = '\0'; |
| s = buf; |
| |
| // puts(buf); |
| |
| attrs = ptr; |
| while (*ptr && *ptr != '>') |
| ptr ++; |
| |
| if (*ptr == '>') |
| ptr ++; |
| |
| if (strcasecmp(buf, "HEAD") == 0) |
| head = 1; |
| else if (strcasecmp(buf, "/HEAD") == 0) |
| head = 0; |
| else if (strcasecmp(buf, "TITLE") == 0) |
| { |
| // Copy the title in the document... |
| for (s = title_; |
| *ptr != '<' && *ptr && s < (title_ + sizeof(title_) - 1); |
| *s++ = *ptr++); |
| |
| *s = '\0'; |
| s = buf; |
| } |
| else if (strcasecmp(buf, "A") == 0) |
| { |
| if (get_attr(attrs, "NAME", attr, sizeof(attr)) != NULL) |
| add_target(attr, yy - fsize - 2); |
| |
| if (get_attr(attrs, "HREF", attr, sizeof(attr)) != NULL) |
| strlcpy(linkdest, attr, sizeof(linkdest)); |
| } |
| else if (strcasecmp(buf, "/A") == 0) |
| linkdest[0] = '\0'; |
| else if (strcasecmp(buf, "BODY") == 0) |
| { |
| bgcolor_ = get_color(get_attr(attrs, "BGCOLOR", attr, sizeof(attr)), |
| color()); |
| textcolor_ = get_color(get_attr(attrs, "TEXT", attr, sizeof(attr)), |
| textcolor()); |
| linkcolor_ = get_color(get_attr(attrs, "LINK", attr, sizeof(attr)), |
| fl_contrast(FL_BLUE, color())); |
| } |
| else if (strcasecmp(buf, "BR") == 0) |
| { |
| line = do_align(block, line, xx, newalign, links); |
| xx = block->x; |
| block->h += hh; |
| yy += hh; |
| hh = 0; |
| } |
| else if (strcasecmp(buf, "CENTER") == 0 || |
| strcasecmp(buf, "P") == 0 || |
| strcasecmp(buf, "H1") == 0 || |
| strcasecmp(buf, "H2") == 0 || |
| strcasecmp(buf, "H3") == 0 || |
| strcasecmp(buf, "H4") == 0 || |
| strcasecmp(buf, "H5") == 0 || |
| strcasecmp(buf, "H6") == 0 || |
| strcasecmp(buf, "UL") == 0 || |
| strcasecmp(buf, "OL") == 0 || |
| strcasecmp(buf, "DL") == 0 || |
| strcasecmp(buf, "LI") == 0 || |
| strcasecmp(buf, "DD") == 0 || |
| strcasecmp(buf, "DT") == 0 || |
| strcasecmp(buf, "HR") == 0 || |
| strcasecmp(buf, "PRE") == 0 || |
| strcasecmp(buf, "TABLE") == 0) |
| { |
| block->end = start; |
| line = do_align(block, line, xx, newalign, links); |
| newalign = strcasecmp(buf, "CENTER") ? LEFT : CENTER; |
| xx = block->x; |
| block->h += hh; |
| |
| if (strcasecmp(buf, "UL") == 0 || |
| strcasecmp(buf, "OL") == 0 || |
| strcasecmp(buf, "DL") == 0) |
| { |
| block->h += fsize + 2; |
| xx = margins.push(4 * fsize); |
| } |
| else if (strcasecmp(buf, "TABLE") == 0) |
| { |
| if (get_attr(attrs, "BORDER", attr, sizeof(attr))) |
| border = (uchar)atoi(attr); |
| else |
| border = 0; |
| |
| tc = rc = get_color(get_attr(attrs, "BGCOLOR", attr, sizeof(attr)), bgcolor_); |
| |
| block->h += fsize + 2; |
| |
| format_table(&table_width, columns, start); |
| |
| if ((xx + table_width) > hsize_) { |
| #ifdef DEBUG |
| printf("xx=%d, table_width=%d, hsize_=%d\n", xx, table_width, |
| hsize_); |
| #endif // DEBUG |
| hsize_ = xx + table_width; |
| done = 0; |
| break; |
| } |
| |
| switch (get_align(attrs, talign)) |
| { |
| default : |
| table_offset = 0; |
| break; |
| |
| case CENTER : |
| table_offset = (hsize_ - table_width) / 2 - textsize_; |
| break; |
| |
| case RIGHT : |
| table_offset = hsize_ - table_width - textsize_; |
| break; |
| } |
| |
| column = 0; |
| } |
| |
| if (tolower(buf[0]) == 'h' && isdigit(buf[1])) |
| { |
| font = FL_HELVETICA_BOLD; |
| fsize = textsize_ + '7' - buf[1]; |
| } |
| else if (strcasecmp(buf, "DT") == 0) |
| { |
| font = textfont_ | FL_ITALIC; |
| fsize = textsize_; |
| } |
| else if (strcasecmp(buf, "PRE") == 0) |
| { |
| font = FL_COURIER; |
| fsize = textsize_; |
| pre = 1; |
| } |
| else |
| { |
| font = textfont_; |
| fsize = textsize_; |
| } |
| |
| pushfont(font, fsize); |
| |
| yy = block->y + block->h; |
| hh = 0; |
| |
| if ((tolower(buf[0]) == 'h' && isdigit(buf[1])) || |
| strcasecmp(buf, "DD") == 0 || |
| strcasecmp(buf, "DT") == 0 || |
| strcasecmp(buf, "P") == 0) |
| yy += fsize + 2; |
| else if (strcasecmp(buf, "HR") == 0) |
| { |
| hh += 2 * fsize; |
| yy += fsize; |
| } |
| |
| if (row) |
| block = add_block(start, xx, yy, block->w, 0); |
| else |
| block = add_block(start, xx, yy, hsize_, 0); |
| |
| needspace = 0; |
| line = 0; |
| |
| if (strcasecmp(buf, "CENTER") == 0) |
| newalign = talign = CENTER; |
| else |
| newalign = get_align(attrs, talign); |
| } |
| else if (strcasecmp(buf, "/CENTER") == 0 || |
| strcasecmp(buf, "/P") == 0 || |
| strcasecmp(buf, "/H1") == 0 || |
| strcasecmp(buf, "/H2") == 0 || |
| strcasecmp(buf, "/H3") == 0 || |
| strcasecmp(buf, "/H4") == 0 || |
| strcasecmp(buf, "/H5") == 0 || |
| strcasecmp(buf, "/H6") == 0 || |
| strcasecmp(buf, "/PRE") == 0 || |
| strcasecmp(buf, "/UL") == 0 || |
| strcasecmp(buf, "/OL") == 0 || |
| strcasecmp(buf, "/DL") == 0 || |
| strcasecmp(buf, "/TABLE") == 0) |
| { |
| line = do_align(block, line, xx, newalign, links); |
| xx = block->x; |
| block->end = ptr; |
| |
| if (strcasecmp(buf, "/UL") == 0 || |
| strcasecmp(buf, "/OL") == 0 || |
| strcasecmp(buf, "/DL") == 0) |
| { |
| xx = margins.pop(); |
| block->h += fsize + 2; |
| } |
| else if (strcasecmp(buf, "/TABLE") == 0) |
| { |
| block->h += fsize + 2; |
| xx = margins.current(); |
| } |
| else if (strcasecmp(buf, "/PRE") == 0) |
| { |
| pre = 0; |
| hh = 0; |
| } |
| else if (strcasecmp(buf, "/CENTER") == 0) |
| talign = LEFT; |
| |
| popfont(font, fsize, fcolor); |
| |
| //#if defined(__GNUC__) |
| //#warning FIXME this isspace & 255 test will probably not work on a utf8 stream... And we use it everywhere! |
| //#endif /*__GNUC__*/ |
| while (isspace((*ptr)&255)) |
| ptr ++; |
| |
| block->h += hh; |
| yy += hh; |
| |
| if (tolower(buf[2]) == 'l') |
| yy += fsize + 2; |
| |
| if (row) |
| block = add_block(ptr, xx, yy, block->w, 0); |
| else |
| block = add_block(ptr, xx, yy, hsize_, 0); |
| |
| needspace = 0; |
| hh = 0; |
| line = 0; |
| newalign = talign; |
| } |
| else if (strcasecmp(buf, "TR") == 0) |
| { |
| block->end = start; |
| line = do_align(block, line, xx, newalign, links); |
| xx = block->x; |
| block->h += hh; |
| |
| if (row) |
| { |
| yy = blocks_[row].y + blocks_[row].h; |
| |
| for (cell = blocks_ + row + 1; cell <= block; cell ++) |
| if ((cell->y + cell->h) > yy) |
| yy = cell->y + cell->h; |
| |
| block = blocks_ + row; |
| |
| block->h = yy - block->y + 2; |
| |
| for (i = 0; i < column; i ++) |
| if (cells[i]) |
| { |
| cell = blocks_ + cells[i]; |
| cell->h = block->h; |
| } |
| } |
| |
| memset(cells, 0, sizeof(cells)); |
| |
| yy = block->y + block->h - 4; |
| hh = 0; |
| block = add_block(start, xx, yy, hsize_, 0); |
| row = block - blocks_; |
| needspace = 0; |
| column = 0; |
| line = 0; |
| |
| rc = get_color(get_attr(attrs, "BGCOLOR", attr, sizeof(attr)), tc); |
| } |
| else if (strcasecmp(buf, "/TR") == 0 && row) |
| { |
| line = do_align(block, line, xx, newalign, links); |
| block->end = start; |
| block->h += hh; |
| talign = LEFT; |
| |
| xx = blocks_[row].x; |
| yy = blocks_[row].y + blocks_[row].h; |
| |
| for (cell = blocks_ + row + 1; cell <= block; cell ++) |
| if ((cell->y + cell->h) > yy) |
| yy = cell->y + cell->h; |
| |
| block = blocks_ + row; |
| |
| block->h = yy - block->y + 2; |
| |
| for (i = 0; i < column; i ++) |
| if (cells[i]) |
| { |
| cell = blocks_ + cells[i]; |
| cell->h = block->h; |
| } |
| |
| yy = block->y + block->h /*- 4*/; |
| block = add_block(start, xx, yy, hsize_, 0); |
| needspace = 0; |
| row = 0; |
| line = 0; |
| } |
| else if ((strcasecmp(buf, "TD") == 0 || |
| strcasecmp(buf, "TH") == 0) && row) |
| { |
| int colspan; // COLSPAN attribute |
| |
| |
| line = do_align(block, line, xx, newalign, links); |
| block->end = start; |
| block->h += hh; |
| |
| if (strcasecmp(buf, "TH") == 0) |
| font = textfont_ | FL_BOLD; |
| else |
| font = textfont_; |
| |
| fsize = textsize_; |
| |
| xx = blocks_[row].x + fsize + 3 + table_offset; |
| for (i = 0; i < column; i ++) |
| xx += columns[i] + 6; |
| |
| margins.push(xx - margins.current()); |
| |
| if (get_attr(attrs, "COLSPAN", attr, sizeof(attr)) != NULL) |
| colspan = atoi(attr); |
| else |
| colspan = 1; |
| |
| for (i = 0, ww = -6; i < colspan; i ++) |
| ww += columns[column + i] + 6; |
| |
| if (block->end == block->start && nblocks_ > 1) |
| { |
| nblocks_ --; |
| block --; |
| } |
| |
| pushfont(font, fsize); |
| |
| yy = blocks_[row].y; |
| hh = 0; |
| block = add_block(start, xx, yy, xx + ww, 0, border); |
| needspace = 0; |
| line = 0; |
| newalign = get_align(attrs, tolower(buf[1]) == 'h' ? CENTER : LEFT); |
| talign = newalign; |
| |
| cells[column] = block - blocks_; |
| |
| column += colspan; |
| |
| block->bgcolor = get_color(get_attr(attrs, "BGCOLOR", attr, |
| sizeof(attr)), rc); |
| } |
| else if ((strcasecmp(buf, "/TD") == 0 || |
| strcasecmp(buf, "/TH") == 0) && row) |
| { |
| line = do_align(block, line, xx, newalign, links); |
| popfont(font, fsize, fcolor); |
| xx = margins.pop(); |
| talign = LEFT; |
| } |
| else if (strcasecmp(buf, "FONT") == 0) |
| { |
| if (get_attr(attrs, "FACE", attr, sizeof(attr)) != NULL) { |
| if (!strncasecmp(attr, "helvetica", 9) || |
| !strncasecmp(attr, "arial", 5) || |
| !strncasecmp(attr, "sans", 4)) font = FL_HELVETICA; |
| else if (!strncasecmp(attr, "times", 5) || |
| !strncasecmp(attr, "serif", 5)) font = FL_TIMES; |
| else if (!strncasecmp(attr, "symbol", 6)) font = FL_SYMBOL; |
| else font = FL_COURIER; |
| } |
| |
| if (get_attr(attrs, "SIZE", attr, sizeof(attr)) != NULL) { |
| if (isdigit(attr[0] & 255)) { |
| // Absolute size |
| fsize = (int)(textsize_ * pow(1.2, atoi(attr) - 3.0)); |
| } else { |
| // Relative size |
| fsize = (int)(fsize * pow(1.2, atoi(attr))); |
| } |
| } |
| |
| pushfont(font, fsize); |
| } |
| else if (strcasecmp(buf, "/FONT") == 0) |
| popfont(font, fsize, fcolor); |
| else if (strcasecmp(buf, "B") == 0 || |
| strcasecmp(buf, "STRONG") == 0) |
| pushfont(font |= FL_BOLD, fsize); |
| else if (strcasecmp(buf, "I") == 0 || |
| strcasecmp(buf, "EM") == 0) |
| pushfont(font |= FL_ITALIC, fsize); |
| else if (strcasecmp(buf, "CODE") == 0 || |
| strcasecmp(buf, "TT") == 0) |
| pushfont(font = FL_COURIER, fsize); |
| else if (strcasecmp(buf, "KBD") == 0) |
| pushfont(font = FL_COURIER_BOLD, fsize); |
| else if (strcasecmp(buf, "VAR") == 0) |
| pushfont(font = FL_COURIER_ITALIC, fsize); |
| else if (strcasecmp(buf, "/B") == 0 || |
| strcasecmp(buf, "/STRONG") == 0 || |
| strcasecmp(buf, "/I") == 0 || |
| strcasecmp(buf, "/EM") == 0 || |
| strcasecmp(buf, "/CODE") == 0 || |
| strcasecmp(buf, "/TT") == 0 || |
| strcasecmp(buf, "/KBD") == 0 || |
| strcasecmp(buf, "/VAR") == 0) |
| popfont(font, fsize, fcolor); |
| else if (strcasecmp(buf, "IMG") == 0) |
| { |
| Fl_Shared_Image *img = 0; |
| int width; |
| int height; |
| |
| |
| get_attr(attrs, "WIDTH", wattr, sizeof(wattr)); |
| get_attr(attrs, "HEIGHT", hattr, sizeof(hattr)); |
| width = get_length(wattr); |
| height = get_length(hattr); |
| |
| if (get_attr(attrs, "SRC", attr, sizeof(attr))) { |
| img = get_image(attr, width, height); |
| width = img->w(); |
| height = img->h(); |
| } |
| |
| ww = width; |
| |
| if (ww > hsize_) { |
| hsize_ = ww; |
| done = 0; |
| break; |
| } |
| |
| if (needspace && xx > block->x) |
| ww += (int)fl_width(' '); |
| |
| if ((xx + ww) > block->w) |
| { |
| line = do_align(block, line, xx, newalign, links); |
| xx = block->x; |
| yy += hh; |
| block->h += hh; |
| hh = 0; |
| } |
| |
| if (linkdest[0]) |
| add_link(linkdest, xx, yy - height, ww, height); |
| |
| xx += ww; |
| if ((height + 2) > hh) |
| hh = height + 2; |
| |
| needspace = 0; |
| } |
| } |
| else if (*ptr == '\n' && pre) |
| { |
| if (linkdest[0]) |
| add_link(linkdest, xx, yy - hh, ww, hh); |
| |
| if (xx > hsize_) { |
| hsize_ = xx; |
| done = 0; |
| break; |
| } |
| |
| line = do_align(block, line, xx, newalign, links); |
| xx = block->x; |
| yy += hh; |
| block->h += hh; |
| needspace = 0; |
| ptr ++; |
| } |
| else if (isspace((*ptr)&255)) |
| { |
| needspace = 1; |
| if ( pre ) { |
| xx += (int)fl_width(' '); |
| } |
| ptr ++; |
| } |
| else if (*ptr == '&' && s < (buf + sizeof(buf) - 1)) |
| { |
| // Handle html '&' codes, eg. "&" |
| ptr ++; |
| |
| int qch = quote_char(ptr); |
| |
| if (qch < 0) |
| *s++ = '&'; |
| else { |
| int l; |
| l = fl_utf8encode((unsigned int) qch, s); |
| if (l < 1) l = 1; |
| s += l; |
| ptr = strchr(ptr, ';') + 1; |
| } |
| |
| if ((fsize + 2) > hh) |
| hh = fsize + 2; |
| } |
| else |
| { |
| if (s < (buf + sizeof(buf) - 1)) |
| *s++ = *ptr++; |
| else |
| ptr ++; |
| |
| if ((fsize + 2) > hh) |
| hh = fsize + 2; |
| } |
| } |
| |
| if (s > buf && !head) |
| { |
| *s = '\0'; |
| ww = (int)fl_width(buf); |
| |
| // printf("line = %d, xx = %d, ww = %d, block->x = %d, block->w = %d\n", |
| // line, xx, ww, block->x, block->w); |
| |
| if (ww > hsize_) { |
| hsize_ = ww; |
| done = 0; |
| break; |
| } |
| |
| if (needspace && xx > block->x) |
| ww += (int)fl_width(' '); |
| |
| if ((xx + ww) > block->w) |
| { |
| line = do_align(block, line, xx, newalign, links); |
| xx = block->x; |
| yy += hh; |
| block->h += hh; |
| hh = 0; |
| } |
| |
| if (linkdest[0]) |
| add_link(linkdest, xx, yy - fsize, ww, fsize); |
| |
| xx += ww; |
| } |
| |
| do_align(block, line, xx, newalign, links); |
| |
| block->end = ptr; |
| size_ = yy + hh; |
| } |
| |
| // printf("margins.depth_=%d\n", margins.depth_); |
| |
| if (ntargets_ > 1) |
| qsort(targets_, ntargets_, sizeof(Fl_Help_Target), |
| (compare_func_t)compare_targets); |
| |
| int dx = Fl::box_dw(b) - Fl::box_dx(b); |
| int dy = Fl::box_dh(b) - Fl::box_dy(b); |
| int ss = scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size(); |
| int dw = Fl::box_dw(b) + ss; |
| int dh = Fl::box_dh(b); |
| |
| if (hsize_ > (w() - dw)) { |
| hscrollbar_.show(); |
| |
| dh += ss; |
| |
| if (size_ < (h() - dh)) { |
| scrollbar_.hide(); |
| hscrollbar_.resize(x() + Fl::box_dx(b), y() + h() - ss - dy, |
| w() - Fl::box_dw(b), ss); |
| } else { |
| scrollbar_.show(); |
| scrollbar_.resize(x() + w() - ss - dx, y() + Fl::box_dy(b), |
| ss, h() - ss - Fl::box_dh(b)); |
| hscrollbar_.resize(x() + Fl::box_dx(b), y() + h() - ss - dy, |
| w() - ss - Fl::box_dw(b), ss); |
| } |
| } else { |
| hscrollbar_.hide(); |
| |
| if (size_ < (h() - dh)) scrollbar_.hide(); |
| else { |
| scrollbar_.resize(x() + w() - ss - dx, y() + Fl::box_dy(b), |
| ss, h() - Fl::box_dh(b)); |
| scrollbar_.show(); |
| } |
| } |
| |
| // Reset scrolling if it needs to be... |
| if (scrollbar_.visible()) { |
| int temph = h() - Fl::box_dh(b); |
| if (hscrollbar_.visible()) temph -= ss; |
| if ((topline_ + temph) > size_) topline(size_ - temph); |
| else topline(topline_); |
| } else topline(0); |
| |
| if (hscrollbar_.visible()) { |
| int tempw = w() - ss - Fl::box_dw(b); |
| if ((leftline_ + tempw) > hsize_) leftline(hsize_ - tempw); |
| else leftline(leftline_); |
| } else leftline(0); |
| } |
| |
| |
| /** Formats a table */ |
| void |
| Fl_Help_View::format_table(int *table_width, // O - Total table width |
| int *columns, // O - Column widths |
| const char *table) // I - Pointer to start of table |
| { |
| int column, // Current column |
| num_columns, // Number of columns |
| colspan, // COLSPAN attribute |
| width, // Current width |
| temp_width, // Temporary width |
| max_width, // Maximum width |
| incell, // In a table cell? |
| pre, // <PRE> text? |
| needspace; // Need whitespace? |
| char *s, // Pointer into buffer |
| buf[1024], // Text buffer |
| attr[1024], // Other attribute |
| wattr[1024], // WIDTH attribute |
| hattr[1024]; // HEIGHT attribute |
| const char *ptr, // Pointer into table |
| *attrs, // Pointer to attributes |
| *start; // Start of element |
| int minwidths[MAX_COLUMNS]; // Minimum widths for each column |
| Fl_Font font; |
| Fl_Fontsize fsize; // Current font and size |
| Fl_Color fcolor; // Currrent font color |
| |
| // Clear widths... |
| *table_width = 0; |
| for (column = 0; column < MAX_COLUMNS; column ++) |
| { |
| columns[column] = 0; |
| minwidths[column] = 0; |
| } |
| |
| num_columns = 0; |
| colspan = 0; |
| max_width = 0; |
| pre = 0; |
| needspace = 0; |
| fstack_.top(font, fsize, fcolor); |
| |
| // Scan the table... |
| for (ptr = table, column = -1, width = 0, s = buf, incell = 0; *ptr;) |
| { |
| if ((*ptr == '<' || isspace((*ptr)&255)) && s > buf && incell) |
| { |
| // Check width... |
| if (needspace) |
| { |
| *s++ = ' '; |
| needspace = 0; |
| } |
| |
| *s = '\0'; |
| temp_width = (int)fl_width(buf); |
| s = buf; |
| |
| if (temp_width > minwidths[column]) |
| minwidths[column] = temp_width; |
| |
| width += temp_width; |
| |
| if (width > max_width) |
| max_width = width; |
| } |
| |
| if (*ptr == '<') |
| { |
| start = ptr; |
| |
| for (s = buf, ptr ++; *ptr && *ptr != '>' && !isspace((*ptr)&255);) |
| if (s < (buf + sizeof(buf) - 1)) |
| *s++ = *ptr++; |
| else |
| ptr ++; |
| |
| *s = '\0'; |
| s = buf; |
| |
| attrs = ptr; |
| while (*ptr && *ptr != '>') |
| ptr ++; |
| |
| if (*ptr == '>') |
| ptr ++; |
| |
| if (strcasecmp(buf, "BR") == 0 || |
| strcasecmp(buf, "HR") == 0) |
| { |
| width = 0; |
| needspace = 0; |
| } |
| else if (strcasecmp(buf, "TABLE") == 0 && start > table) |
| break; |
| else if (strcasecmp(buf, "CENTER") == 0 || |
| strcasecmp(buf, "P") == 0 || |
| strcasecmp(buf, "H1") == 0 || |
| strcasecmp(buf, "H2") == 0 || |
| strcasecmp(buf, "H3") == 0 || |
| strcasecmp(buf, "H4") == 0 || |
| strcasecmp(buf, "H5") == 0 || |
| strcasecmp(buf, "H6") == 0 || |
| strcasecmp(buf, "UL") == 0 || |
| strcasecmp(buf, "OL") == 0 || |
| strcasecmp(buf, "DL") == 0 || |
| strcasecmp(buf, "LI") == 0 || |
| strcasecmp(buf, "DD") == 0 || |
| strcasecmp(buf, "DT") == 0 || |
| strcasecmp(buf, "PRE") == 0) |
| { |
| width = 0; |
| needspace = 0; |
| |
| if (tolower(buf[0]) == 'h' && isdigit(buf[1])) |
| { |
| font = FL_HELVETICA_BOLD; |
| fsize = textsize_ + '7' - buf[1]; |
| } |
| else if (strcasecmp(buf, "DT") == 0) |
| { |
| font = textfont_ | FL_ITALIC; |
| fsize = textsize_; |
| } |
| else if (strcasecmp(buf, "PRE") == 0) |
| { |
| font = FL_COURIER; |
| fsize = textsize_; |
| pre = 1; |
| } |
| else if (strcasecmp(buf, "LI") == 0) |
| { |
| width += 4 * fsize; |
| font = textfont_; |
| fsize = textsize_; |
| } |
| else |
| { |
| font = textfont_; |
| fsize = textsize_; |
| } |
| |
| pushfont(font, fsize); |
| } |
| else if (strcasecmp(buf, "/CENTER") == 0 || |
| strcasecmp(buf, "/P") == 0 || |
| strcasecmp(buf, "/H1") == 0 || |
| strcasecmp(buf, "/H2") == 0 || |
| strcasecmp(buf, "/H3") == 0 || |
| strcasecmp(buf, "/H4") == 0 || |
| strcasecmp(buf, "/H5") == 0 || |
| strcasecmp(buf, "/H6") == 0 || |
| strcasecmp(buf, "/PRE") == 0 || |
| strcasecmp(buf, "/UL") == 0 || |
| strcasecmp(buf, "/OL") == 0 || |
| strcasecmp(buf, "/DL") == 0) |
| { |
| width = 0; |
| needspace = 0; |
| |
| popfont(font, fsize, fcolor); |
| } |
| else if (strcasecmp(buf, "TR") == 0 || strcasecmp(buf, "/TR") == 0 || |
| strcasecmp(buf, "/TABLE") == 0) |
| { |
| // printf("%s column = %d, colspan = %d, num_columns = %d\n", |
| // buf, column, colspan, num_columns); |
| |
| if (column >= 0) |
| { |
| // This is a hack to support COLSPAN... |
| max_width /= colspan; |
| |
| while (colspan > 0) |
| { |
| if (max_width > columns[column]) |
| columns[column] = max_width; |
| |
| column ++; |
| colspan --; |
| } |
| } |
| |
| if (strcasecmp(buf, "/TABLE") == 0) |
| break; |
| |
| needspace = 0; |
| column = -1; |
| width = 0; |
| max_width = 0; |
| incell = 0; |
| } |
| else if (strcasecmp(buf, "TD") == 0 || |
| strcasecmp(buf, "TH") == 0) |
| { |
| // printf("BEFORE column = %d, colspan = %d, num_columns = %d\n", |
| // column, colspan, num_columns); |
| |
| if (column >= 0) |
| { |
| // This is a hack to support COLSPAN... |
| max_width /= colspan; |
| |
| while (colspan > 0) |
| { |
| if (max_width > columns[column]) |
| columns[column] = max_width; |
| |
| column ++; |
| colspan --; |
| } |
| } |
| else |
| column ++; |
| |
| if (get_attr(attrs, "COLSPAN", attr, sizeof(attr)) != NULL) |
| colspan = atoi(attr); |
| else |
| colspan = 1; |
| |
| // printf("AFTER column = %d, colspan = %d, num_columns = %d\n", |
| // column, colspan, num_columns); |
| |
| if ((column + colspan) >= num_columns) |
| num_columns = column + colspan; |
| |
| needspace = 0; |
| width = 0; |
| incell = 1; |
| |
| if (strcasecmp(buf, "TH") == 0) |
| font = textfont_ | FL_BOLD; |
| else |
| font = textfont_; |
| |
| fsize = textsize_; |
| |
| pushfont(font, fsize); |
| |
| if (get_attr(attrs, "WIDTH", attr, sizeof(attr)) != NULL) |
| max_width = get_length(attr); |
| else |
| max_width = 0; |
| |
| // printf("max_width = %d\n", max_width); |
| } |
| else if (strcasecmp(buf, "/TD") == 0 || |
| strcasecmp(buf, "/TH") == 0) |
| { |
| incell = 0; |
| popfont(font, fsize, fcolor); |
| } |
| else if (strcasecmp(buf, "B") == 0 || |
| strcasecmp(buf, "STRONG") == 0) |
| pushfont(font |= FL_BOLD, fsize); |
| else if (strcasecmp(buf, "I") == 0 || |
| strcasecmp(buf, "EM") == 0) |
| pushfont(font |= FL_ITALIC, fsize); |
| else if (strcasecmp(buf, "CODE") == 0 || |
| strcasecmp(buf, "TT") == 0) |
| pushfont(font = FL_COURIER, fsize); |
| else if (strcasecmp(buf, "KBD") == 0) |
| pushfont(font = FL_COURIER_BOLD, fsize); |
| else if (strcasecmp(buf, "VAR") == 0) |
| pushfont(font = FL_COURIER_ITALIC, fsize); |
| else if (strcasecmp(buf, "/B") == 0 || |
| strcasecmp(buf, "/STRONG") == 0 || |
| strcasecmp(buf, "/I") == 0 || |
| strcasecmp(buf, "/EM") == 0 || |
| strcasecmp(buf, "/CODE") == 0 || |
| strcasecmp(buf, "/TT") == 0 || |
| strcasecmp(buf, "/KBD") == 0 || |
| strcasecmp(buf, "/VAR") == 0) |
| popfont(font, fsize, fcolor); |
| else if (strcasecmp(buf, "IMG") == 0 && incell) |
| { |
| Fl_Shared_Image *img = 0; |
| int iwidth, iheight; |
| |
| |
| get_attr(attrs, "WIDTH", wattr, sizeof(wattr)); |
| get_attr(attrs, "HEIGHT", hattr, sizeof(hattr)); |
| iwidth = get_length(wattr); |
| iheight = get_length(hattr); |
| |
| if (get_attr(attrs, "SRC", attr, sizeof(attr))) { |
| img = get_image(attr, iwidth, iheight); |
| iwidth = img->w(); |
| iheight = img->h(); |
| } |
| |
| if (iwidth > minwidths[column]) |
| minwidths[column] = iwidth; |
| |
| width += iwidth; |
| if (needspace) |
| width += (int)fl_width(' '); |
| |
| if (width > max_width) |
| max_width = width; |
| |
| needspace = 0; |
| } |
| } |
| else if (*ptr == '\n' && pre) |
| { |
| width = 0; |
| needspace = 0; |
| ptr ++; |
| } |
| else if (isspace((*ptr)&255)) |
| { |
| needspace = 1; |
| |
| ptr ++; |
| } |
| else if (*ptr == '&' && s < (buf + sizeof(buf) - 1)) |
| { |
| ptr ++; |
| |
| int qch = quote_char(ptr); |
| |
| if (qch < 0) |
| *s++ = '&'; |
| else { |
| // int l; |
| // l = fl_utf8encode((unsigned int) qch, s); |
| // if (l < 1) l = 1; |
| // s += l; |
| *s++ = qch; |
| ptr = strchr(ptr, ';') + 1; |
| } |
| } |
| else |
| { |
| if (s < (buf + sizeof(buf) - 1)) |
| *s++ = *ptr++; |
| else |
| ptr ++; |
| } |
| } |
| |
| // Now that we have scanned the entire table, adjust the table and |
| // cell widths to fit on the screen... |
| if (get_attr(table + 6, "WIDTH", attr, sizeof(attr))) |
| *table_width = get_length(attr); |
| else |
| *table_width = 0; |
| |
| #ifdef DEBUG |
| printf("num_columns = %d, table_width = %d\n", num_columns, *table_width); |
| #endif // DEBUG |
| |
| if (num_columns == 0) |
| return; |
| |
| // Add up the widths... |
| for (column = 0, width = 0; column < num_columns; column ++) |
| width += columns[column]; |
| |
| #ifdef DEBUG |
| printf("width = %d, w() = %d\n", width, w()); |
| for (column = 0; column < num_columns; column ++) |
| printf(" columns[%d] = %d, minwidths[%d] = %d\n", column, columns[column], |
| column, minwidths[column]); |
| #endif // DEBUG |
| |
| // Adjust the width if needed... |
| int scale_width = *table_width; |
| |
| int scrollsize = scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size(); |
| if (scale_width == 0) { |
| if (width > (hsize_ - scrollsize)) scale_width = hsize_ - scrollsize; |
| else scale_width = width; |
| } |
| |
| if (width < scale_width) { |
| #ifdef DEBUG |
| printf("Scaling table up to %d from %d...\n", scale_width, width); |
| #endif // DEBUG |
| |
| *table_width = 0; |
| |
| scale_width = (scale_width - width) / num_columns; |
| |
| #ifdef DEBUG |
| printf("adjusted scale_width = %d\n", scale_width); |
| #endif // DEBUG |
| |
| for (column = 0; column < num_columns; column ++) { |
| columns[column] += scale_width; |
| |
| (*table_width) += columns[column]; |
| } |
| } |
| else if (width > scale_width) { |
| #ifdef DEBUG |
| printf("Scaling table down to %d from %d...\n", scale_width, width); |
| #endif // DEBUG |
| |
| for (column = 0; column < num_columns; column ++) { |
| width -= minwidths[column]; |
| scale_width -= minwidths[column]; |
| } |
| |
| #ifdef DEBUG |
| printf("adjusted width = %d, scale_width = %d\n", width, scale_width); |
| #endif // DEBUG |
| |
| if (width > 0) { |
| for (column = 0; column < num_columns; column ++) { |
| columns[column] -= minwidths[column]; |
| columns[column] = scale_width * columns[column] / width; |
| columns[column] += minwidths[column]; |
| } |
| } |
| |
| *table_width = 0; |
| for (column = 0; column < num_columns; column ++) { |
| (*table_width) += columns[column]; |
| } |
| } |
| else if (*table_width == 0) |
| *table_width = width; |
| |
| #ifdef DEBUG |
| printf("FINAL table_width = %d\n", *table_width); |
| for (column = 0; column < num_columns; column ++) |
| printf(" columns[%d] = %d\n", column, columns[column]); |
| #endif // DEBUG |
| } |
| |
| |
| /** Frees memory used for the document. */ |
| void |
| Fl_Help_View::free_data() { |
| // Release all images... |
| if (value_) { |
| const char *ptr, // Pointer into block |
| *attrs; // Pointer to start of element attributes |
| char *s, // Pointer into buffer |
| buf[1024], // Text buffer |
| attr[1024], // Attribute buffer |
| wattr[1024], // Width attribute buffer |
| hattr[1024]; // Height attribute buffer |
| |
| for (ptr = value_; *ptr;) |
| { |
| if (*ptr == '<') |
| { |
| ptr ++; |
| |
| if (strncmp(ptr, "!--", 3) == 0) |
| { |
| // Comment... |
| ptr += 3; |
| if ((ptr = strstr(ptr, "-->")) != NULL) |
| { |
| ptr += 3; |
| continue; |
| } |
| else |
| break; |
| } |
| |
| s = buf; |
| |
| while (*ptr && *ptr != '>' && !isspace((*ptr)&255)) |
| if (s < (buf + sizeof(buf) - 1)) |
| *s++ = *ptr++; |
| else |
| ptr ++; |
| |
| *s = '\0'; |
| |
| attrs = ptr; |
| while (*ptr && *ptr != '>') |
| ptr ++; |
| |
| if (*ptr == '>') |
| ptr ++; |
| |
| if (strcasecmp(buf, "IMG") == 0) |
| { |
| Fl_Shared_Image *img; |
| int width; |
| int height; |
| |
| get_attr(attrs, "WIDTH", wattr, sizeof(wattr)); |
| get_attr(attrs, "HEIGHT", hattr, sizeof(hattr)); |
| width = get_length(wattr); |
| height = get_length(hattr); |
| |
| if (get_attr(attrs, "SRC", attr, sizeof(attr))) { |
| // Get and release the image to free it from memory... |
| img = get_image(attr, width, height); |
| if ((void*)img != &broken_image) { |
| img->release(); |
| } |
| } |
| } |
| } |
| else |
| ptr ++; |
| } |
| |
| free((void *)value_); |
| value_ = 0; |
| } |
| |
| // Free all of the arrays... |
| if (nblocks_) { |
| free(blocks_); |
| |
| ablocks_ = 0; |
| nblocks_ = 0; |
| blocks_ = 0; |
| } |
| |
| if (nlinks_) { |
| free(links_); |
| |
| alinks_ = 0; |
| nlinks_ = 0; |
| links_ = 0; |
| } |
| |
| if (ntargets_) { |
| free(targets_); |
| |
| atargets_ = 0; |
| ntargets_ = 0; |
| targets_ = 0; |
| } |
| } |
| |
| /** Gets an alignment attribute. */ |
| int // O - Alignment |
| Fl_Help_View::get_align(const char *p, // I - Pointer to start of attrs |
| int a) // I - Default alignment |
| { |
| char buf[255]; // Alignment value |
| |
| |
| if (get_attr(p, "ALIGN", buf, sizeof(buf)) == NULL) |
| return (a); |
| |
| if (strcasecmp(buf, "CENTER") == 0) |
| return (CENTER); |
| else if (strcasecmp(buf, "RIGHT") == 0) |
| return (RIGHT); |
| else |
| return (LEFT); |
| } |
| |
| |
| /** Gets an attribute value from the string. */ |
| const char * // O - Pointer to buf or NULL |
| Fl_Help_View::get_attr(const char *p, // I - Pointer to start of attributes |
| const char *n, // I - Name of attribute |
| char *buf, // O - Buffer for attribute value |
| int bufsize) // I - Size of buffer |
| { |
| char name[255], // Name from string |
| *ptr, // Pointer into name or value |
| quote; // Quote |
| |
| |
| buf[0] = '\0'; |
| |
| while (*p && *p != '>') |
| { |
| while (isspace((*p)&255)) |
| p ++; |
| |
| if (*p == '>' || !*p) |
| return (NULL); |
| |
| for (ptr = name; *p && !isspace((*p)&255) && *p != '=' && *p != '>';) |
| if (ptr < (name + sizeof(name) - 1)) |
| *ptr++ = *p++; |
| else |
| p ++; |
| |
| *ptr = '\0'; |
| |
| if (isspace((*p)&255) || !*p || *p == '>') |
| buf[0] = '\0'; |
| else |
| { |
| if (*p == '=') |
| p ++; |
| |
| for (ptr = buf; *p && !isspace((*p)&255) && *p != '>';) |
| if (*p == '\'' || *p == '\"') |
| { |
| quote = *p++; |
| |
| while (*p && *p != quote) |
| if ((ptr - buf + 1) < bufsize) |
| *ptr++ = *p++; |
| else |
| p ++; |
| |
| if (*p == quote) |
| p ++; |
| } |
| else if ((ptr - buf + 1) < bufsize) |
| *ptr++ = *p++; |
| else |
| p ++; |
| |
| *ptr = '\0'; |
| } |
| |
| if (strcasecmp(n, name) == 0) |
| return (buf); |
| else |
| buf[0] = '\0'; |
| |
| if (*p == '>') |
| return (NULL); |
| } |
| |
| return (NULL); |
| } |
| |
| |
| /** Gets a color attribute. */ |
| Fl_Color // O - Color value |
| Fl_Help_View::get_color(const char *n, // I - Color name |
| Fl_Color c) // I - Default color value |
| { |
| int i; // Looping var |
| int rgb, r, g, b; // RGB values |
| static const struct { // Color name table |
| const char *name; |
| int r, g, b; |
| } colors[] = { |
| { "black", 0x00, 0x00, 0x00 }, |
| { "red", 0xff, 0x00, 0x00 }, |
| { "green", 0x00, 0x80, 0x00 }, |
| { "yellow", 0xff, 0xff, 0x00 }, |
| { "blue", 0x00, 0x00, 0xff }, |
| { "magenta", 0xff, 0x00, 0xff }, |
| { "fuchsia", 0xff, 0x00, 0xff }, |
| { "cyan", 0x00, 0xff, 0xff }, |
| { "aqua", 0x00, 0xff, 0xff }, |
| { "white", 0xff, 0xff, 0xff }, |
| { "gray", 0x80, 0x80, 0x80 }, |
| { "grey", 0x80, 0x80, 0x80 }, |
| { "lime", 0x00, 0xff, 0x00 }, |
| { "maroon", 0x80, 0x00, 0x00 }, |
| { "navy", 0x00, 0x00, 0x80 }, |
| { "olive", 0x80, 0x80, 0x00 }, |
| { "purple", 0x80, 0x00, 0x80 }, |
| { "silver", 0xc0, 0xc0, 0xc0 }, |
| { "teal", 0x00, 0x80, 0x80 } |
| }; |
| |
| |
| if (!n || !n[0]) return c; |
| |
| if (n[0] == '#') { |
| // Do hex color lookup |
| rgb = strtol(n + 1, NULL, 16); |
| |
| if (strlen(n) > 4) { |
| r = rgb >> 16; |
| g = (rgb >> 8) & 255; |
| b = rgb & 255; |
| } else { |
| r = (rgb >> 8) * 17; |
| g = ((rgb >> 4) & 15) * 17; |
| b = (rgb & 15) * 17; |
| } |
| return (fl_rgb_color((uchar)r, (uchar)g, (uchar)b)); |
| } else { |
| for (i = 0; i < (int)(sizeof(colors) / sizeof(colors[0])); i ++) |
| if (!strcasecmp(n, colors[i].name)) { |
| return fl_rgb_color(colors[i].r, colors[i].g, colors[i].b); |
| } |
| return c; |
| } |
| } |
| |
| |
| /** Gets an inline image. |
| |
| The image reference count is maintained accordingly, such that |
| the image can be released exactly once when the document is closed. |
| |
| \return a pointer to a cached Fl_Shared_Image, if the image can be loaded, |
| otherwise a pointer to an internal Fl_Pixmap (broken_image). |
| |
| \todo Fl_Help_View::get_image() returns a pointer to the internal |
| Fl_Pixmap broken_image, but this is _not_ compatible with the |
| return type Fl_Shared_Image (release() must not be called). |
| */ |
| |
| /* Implementation note: (A.S. Apr 05, 2009) |
| |
| Fl_Help_View::get_image() uses a static global flag (initial_load) |
| to determine, if it is called from the initial loading of a document |
| (load() or value()), or from resize() or draw(). |
| |
| A better solution would be to manage all loaded images in an own |
| structure like Fl_Help_Target (Fl_Help_Image ?) to avoid using this |
| global flag, but this would break the ABI ! |
| |
| This should be fixed in FLTK 1.3 ! |
| |
| |
| If initial_load is true, then Fl_Shared_Image::get() is called to |
| load the image, and the reference count of the shared image is |
| increased by one. |
| |
| If initial_load is false, then Fl_Shared_Image::find() is called to |
| load the image, and the image is released immediately. This avoids |
| increasing the reference count when calling get_image() from draw() |
| or resize(). |
| |
| Calling Fl_Shared_Image::find() instead of Fl_Shared_Image::get() avoids |
| doing unnecessary i/o for "broken images" within each resize/redraw. |
| |
| Each image must be released exactly once in the destructor or before |
| a new document is loaded: see free_data(). |
| */ |
| |
| Fl_Shared_Image * |
| Fl_Help_View::get_image(const char *name, int W, int H) { |
| const char *localname; // Local filename |
| char dir[FL_PATH_MAX]; // Current directory |
| char temp[FL_PATH_MAX], // Temporary filename |
| *tempptr; // Pointer into temporary name |
| Fl_Shared_Image *ip; // Image pointer... |
| |
| // See if the image can be found... |
| if (strchr(directory_, ':') != NULL && strchr(name, ':') == NULL) { |
| if (name[0] == '/') { |
| strlcpy(temp, directory_, sizeof(temp)); |
| |
| if ((tempptr = strrchr(strchr(directory_, ':') + 3, '/')) != NULL) { |
| strlcpy(tempptr, name, sizeof(temp) - (tempptr - temp)); |
| } else { |
| strlcat(temp, name, sizeof(temp)); |
| } |
| } else { |
| snprintf(temp, sizeof(temp), "%s/%s", directory_, name); |
| } |
| |
| if (link_) localname = (*link_)(this, temp); |
| else localname = temp; |
| } else if (name[0] != '/' && strchr(name, ':') == NULL) { |
| if (directory_[0]) snprintf(temp, sizeof(temp), "%s/%s", directory_, name); |
| else { |
| fl_getcwd(dir, sizeof(dir)); |
| snprintf(temp, sizeof(temp), "file:%s/%s", dir, name); |
| } |
| |
| if (link_) localname = (*link_)(this, temp); |
| else localname = temp; |
| } else if (link_) localname = (*link_)(this, name); |
| else localname = name; |
| |
| if (!localname) return 0; |
| |
| if (strncmp(localname, "file:", 5) == 0) localname += 5; |
| |
| if (initial_load) { |
| if ((ip = Fl_Shared_Image::get(localname, W, H)) == NULL) { |
| ip = (Fl_Shared_Image *)&broken_image; |
| } |
| } else { // draw or resize |
| if ((ip = Fl_Shared_Image::find(localname, W, H)) == NULL) { |
| ip = (Fl_Shared_Image *)&broken_image; |
| } else { |
| ip->release(); |
| } |
| } |
| |
| return ip; |
| } |
| |
| |
| /** Gets a length value, either absolute or %. */ |
| int |
| Fl_Help_View::get_length(const char *l) { // I - Value |
| int val; // Integer value |
| |
| if (!l[0]) return 0; |
| |
| val = atoi(l); |
| if (l[strlen(l) - 1] == '%') { |
| if (val > 100) val = 100; |
| else if (val < 0) val = 0; |
| |
| int scrollsize = scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size(); |
| val = val * (hsize_ - scrollsize) / 100; |
| } |
| |
| return val; |
| } |
| |
| |
| Fl_Help_Link *Fl_Help_View::find_link(int xx, int yy) |
| { |
| int i; |
| Fl_Help_Link *linkp; |
| for (i = nlinks_, linkp = links_; i > 0; i --, linkp ++) { |
| if (xx >= linkp->x && xx < linkp->w && |
| yy >= linkp->y && yy < linkp->h) |
| break; |
| } |
| return i ? linkp : 0L; |
| } |
| |
| void Fl_Help_View::follow_link(Fl_Help_Link *linkp) |
| { |
| char target[32]; // Current target |
| |
| clear_selection(); |
| |
| strlcpy(target, linkp->name, sizeof(target)); |
| |
| set_changed(); |
| |
| if (strcmp(linkp->filename, filename_) != 0 && linkp->filename[0]) |
| { |
| char dir[FL_PATH_MAX]; // Current directory |
| char temp[FL_PATH_MAX], // Temporary filename |
| *tempptr; // Pointer into temporary filename |
| |
| |
| if (strchr(directory_, ':') != NULL && |
| strchr(linkp->filename, ':') == NULL) |
| { |
| if (linkp->filename[0] == '/') |
| { |
| strlcpy(temp, directory_, sizeof(temp)); |
| if ((tempptr = strrchr(strchr(directory_, ':') + 3, '/')) != NULL) |
| strlcpy(tempptr, linkp->filename, sizeof(temp)); |
| else |
| strlcat(temp, linkp->filename, sizeof(temp)); |
| } |
| else |
| snprintf(temp, sizeof(temp), "%s/%s", directory_, linkp->filename); |
| } |
| else if (linkp->filename[0] != '/' && strchr(linkp->filename, ':') == NULL) |
| { |
| if (directory_[0]) |
| snprintf(temp, sizeof(temp), "%s/%s", directory_, linkp->filename); |
| else |
| { |
| fl_getcwd(dir, sizeof(dir)); |
| snprintf(temp, sizeof(temp), "file:%s/%s", dir, linkp->filename); |
| } |
| } |
| else |
| strlcpy(temp, linkp->filename, sizeof(temp)); |
| |
| if (linkp->name[0]) |
| snprintf(temp + strlen(temp), sizeof(temp) - strlen(temp), "#%s", |
| linkp->name); |
| |
| load(temp); |
| } |
| else if (target[0]) |
| topline(target); |
| else |
| topline(0); |
| |
| leftline(0); |
| } |
| |
| /** Removes the current text selection. */ |
| void Fl_Help_View::clear_selection() |
| { |
| if (current_view==this) |
| clear_global_selection(); |
| } |
| /** Selects all the text in the view. */ |
| void Fl_Help_View::select_all() |
| { |
| clear_global_selection(); |
| if (!value_) return; |
| current_view = this; |
| selection_drag_last = selection_last = strlen(value_); |
| selected = 1; |
| } |
| |
| void Fl_Help_View::clear_global_selection() |
| { |
| if (selected) redraw(); |
| selection_push_first = selection_push_last = 0; |
| selection_drag_first = selection_drag_last = 0; |
| selection_first = selection_last = 0; |
| selected = 0; |
| } |
| |
| char Fl_Help_View::begin_selection() |
| { |
| clear_global_selection(); |
| |
| if (!fl_help_view_buffer) fl_help_view_buffer = fl_create_offscreen(1, 1); |
| |
| mouse_x = Fl::event_x(); |
| mouse_y = Fl::event_y(); |
| draw_mode = 1; |
| |
| current_view = this; |
| fl_begin_offscreen(fl_help_view_buffer); |
| draw(); |
| fl_end_offscreen(); |
| |
| draw_mode = 0; |
| |
| if (selection_push_last) return 1; |
| else return 0; |
| } |
| |
| char Fl_Help_View::extend_selection() |
| { |
| if (Fl::event_is_click()) |
| return 0; |
| |
| // printf("old selection_first=%d, selection_last=%d\n", |
| // selection_first, selection_last); |
| |
| int sf = selection_first, sl = selection_last; |
| |
| selected = 1; |
| mouse_x = Fl::event_x(); |
| mouse_y = Fl::event_y(); |
| draw_mode = 2; |
| |
| fl_begin_offscreen(fl_help_view_buffer); |
| draw(); |
| fl_end_offscreen(); |
| |
| draw_mode = 0; |
| |
| if (selection_push_first < selection_drag_first) { |
| selection_first = selection_push_first; |
| } else { |
| selection_first = selection_drag_first; |
| } |
| |
| if (selection_push_last > selection_drag_last) { |
| selection_last = selection_push_last; |
| } else { |
| selection_last = selection_drag_last; |
| } |
| |
| // printf("new selection_first=%d, selection_last=%d\n", |
| // selection_first, selection_last); |
| |
| if (sf!=selection_first || sl!=selection_last) { |
| // puts("REDRAW!!!\n"); |
| return 1; |
| } else { |
| // puts(""); |
| return 0; |
| } |
| } |
| |
| // convert a command with up to four letters into an unsigned int |
| static unsigned int command(const char *cmd) |
| { |
| unsigned int ret = (tolower(cmd[0])<<24); |
| char c = cmd[1]; |
| if (c=='>' || c==' ' || c==0) return ret; |
| ret |= (tolower(c)<<16); |
| c = cmd[2]; |
| if (c=='>' || c==' ' || c==0) return ret; |
| ret |= (tolower(c)<<8); |
| c = cmd[3]; |
| if (c=='>' || c==' ' || c==0) return ret; |
| ret |= tolower(c); |
| c = cmd[4]; |
| if (c=='>' || c==' ' || c==0) return ret; |
| return 0; |
| } |
| |
| #define CMD(a, b, c, d) ((a<<24)|(b<<16)|(c<<8)|d) |
| |
| void Fl_Help_View::end_selection(int clipboard) |
| { |
| if (!selected || current_view!=this) |
| return; |
| // convert the select part of our html text into some kind of somewhat readable ASCII |
| // and store it in the selection buffer |
| char p = 0, pre = 0;; |
| int len = strlen(value_); |
| char *txt = (char*)malloc(len+1), *d = txt; |
| const char *s = value_, *cmd, *src; |
| for (;;) { |
| char c = *s++; |
| if (c==0) break; |
| if (c=='<') { // begin of some html command. Skip until we find a '>' |
| cmd = s; |
| for (;;) { |
| c = *s++; |
| if (c==0 || c=='>') break; |
| } |
| if (c==0) break; |
| // do something with this command... . |
| // the replacement string must not be longer that the command itself plus '<' and '>' |
| src = 0; |
| switch (command(cmd)) { |
| case CMD('p','r','e', 0 ): pre = 1; break; |
| case CMD('/','p','r','e'): pre = 0; break; |
| case CMD('t','d', 0 , 0 ): |
| case CMD('p', 0 , 0 , 0 ): |
| case CMD('/','p', 0 , 0 ): |
| case CMD('b','r', 0 , 0 ): src = "\n"; break; |
| case CMD('l','i', 0 , 0 ): src = "\n * "; break; |
| case CMD('/','h','1', 0 ): |
| case CMD('/','h','2', 0 ): |
| case CMD('/','h','3', 0 ): |
| case CMD('/','h','4', 0 ): |
| case CMD('/','h','5', 0 ): |
| case CMD('/','h','6', 0 ): src = "\n\n"; break; |
| case CMD('t','r', 0 , 0 ): |
| case CMD('h','1', 0 , 0 ): |
| case CMD('h','2', 0 , 0 ): |
| case CMD('h','3', 0 , 0 ): |
| case CMD('h','4', 0 , 0 ): |
| case CMD('h','5', 0 , 0 ): |
| case CMD('h','6', 0 , 0 ): src = "\n\n"; break; |
| case CMD('d','t', 0 , 0 ): src = "\n "; break; |
| case CMD('d','d', 0 , 0 ): src = "\n - "; break; |
| } |
| int n = s-value_; |
| if (src && n>selection_first && n<=selection_last) { |
| while (*src) { |
| *d++ = *src++; |
| } |
| c = src[-1]; |
| p = isspace(c&255) ? ' ' : c; |
| } |
| continue; |
| } |
| if (c=='&') { // special characters |
| int xx = quote_char(s); |
| if (xx>=0) { |
| c = (char)xx; |
| for (;;) { |
| char cc = *s++; |
| if (!cc || cc==';') break; |
| } |
| } |
| } |
| int n = s-value_; |
| if (n>selection_first && n<=selection_last) { |
| if (!pre && isspace(c&255)) c = ' '; |
| if (p!=' '||c!=' ') |
| *d++ = c; |
| p = c; |
| } |
| } |
| *d = 0; |
| Fl::copy(txt, strlen(txt), clipboard); |
| free(txt); |
| } |
| |
| #define ctrl(x) ((x)&0x1f) |
| |
| /** Handles events in the widget. */ |
| int // O - 1 if we handled it, 0 otherwise |
| Fl_Help_View::handle(int event) // I - Event to handle |
| { |
| static Fl_Help_Link *linkp; // currently clicked link |
| |
| int xx = Fl::event_x() - x() + leftline_; |
| int yy = Fl::event_y() - y() + topline_; |
| |
| switch (event) |
| { |
| case FL_FOCUS: |
| redraw(); |
| return 1; |
| case FL_UNFOCUS: |
| clear_selection(); |
| redraw(); |
| return 1; |
| case FL_ENTER : |
| Fl_Group::handle(event); |
| return 1; |
| case FL_LEAVE : |
| fl_cursor(FL_CURSOR_DEFAULT); |
| break; |
| case FL_MOVE: |
| if (find_link(xx, yy)) fl_cursor(FL_CURSOR_HAND); |
| else fl_cursor(FL_CURSOR_DEFAULT); |
| return 1; |
| case FL_PUSH: |
| if (Fl_Group::handle(event)) return 1; |
| linkp = find_link(xx, yy); |
| if (linkp) { |
| fl_cursor(FL_CURSOR_HAND); |
| return 1; |
| } |
| if (begin_selection()) { |
| fl_cursor(FL_CURSOR_INSERT); |
| return 1; |
| } |
| fl_cursor(FL_CURSOR_DEFAULT); |
| return 1; |
| case FL_DRAG: |
| if (linkp) { |
| if (Fl::event_is_click()) { |
| fl_cursor(FL_CURSOR_HAND); |
| } else { |
| fl_cursor(FL_CURSOR_DEFAULT); // should be "FL_CURSOR_CANCEL" if we had it |
| } |
| return 1; |
| } |
| if (current_view==this && selection_push_last) { |
| if (extend_selection()) redraw(); |
| fl_cursor(FL_CURSOR_INSERT); |
| return 1; |
| } |
| fl_cursor(FL_CURSOR_DEFAULT); |
| return 1; |
| case FL_RELEASE: |
| if (linkp) { |
| if (Fl::event_is_click()) { |
| follow_link(linkp); |
| } |
| fl_cursor(FL_CURSOR_DEFAULT); |
| linkp = 0; |
| return 1; |
| } |
| if (current_view==this && selection_push_last) { |
| end_selection(); |
| return 1; |
| } |
| return 1; |
| case FL_SHORTCUT: { |
| char ascii = Fl::event_text()[0]; |
| switch (ascii) { |
| case ctrl('A'): select_all(); redraw(); return 1; |
| case ctrl('C'): |
| case ctrl('X'): end_selection(1); return 1; |
| } |
| break; } |
| } |
| return (Fl_Group::handle(event)); |
| } |
| |
| /** |
| The constructor creates the Fl_Help_View widget at the specified |
| position and size. |
| */ |
| Fl_Help_View::Fl_Help_View(int xx, // I - Left position |
| int yy, // I - Top position |
| int ww, // I - Width in pixels |
| int hh, // I - Height in pixels |
| const char *l) |
| : Fl_Group(xx, yy, ww, hh, l), |
| scrollbar_(xx + ww - Fl::scrollbar_size(), yy, |
| Fl::scrollbar_size(), hh - Fl::scrollbar_size()), |
| hscrollbar_(xx, yy + hh - Fl::scrollbar_size(), |
| ww - Fl::scrollbar_size(), Fl::scrollbar_size()) |
| { |
| color(FL_BACKGROUND2_COLOR, FL_SELECTION_COLOR); |
| |
| title_[0] = '\0'; |
| defcolor_ = FL_FOREGROUND_COLOR; |
| bgcolor_ = FL_BACKGROUND_COLOR; |
| textcolor_ = FL_FOREGROUND_COLOR; |
| linkcolor_ = FL_SELECTION_COLOR; |
| textfont_ = FL_TIMES; |
| textsize_ = 12; |
| value_ = NULL; |
| |
| ablocks_ = 0; |
| nblocks_ = 0; |
| blocks_ = (Fl_Help_Block *)0; |
| |
| link_ = (Fl_Help_Func *)0; |
| |
| alinks_ = 0; |
| nlinks_ = 0; |
| links_ = (Fl_Help_Link *)0; |
| |
| atargets_ = 0; |
| ntargets_ = 0; |
| targets_ = (Fl_Help_Target *)0; |
| |
| directory_[0] = '\0'; |
| filename_[0] = '\0'; |
| |
| topline_ = 0; |
| leftline_ = 0; |
| size_ = 0; |
| hsize_ = 0; |
| scrollbar_size_ = 0; |
| |
| scrollbar_.value(0, hh, 0, 1); |
| scrollbar_.step(8.0); |
| scrollbar_.show(); |
| scrollbar_.callback(scrollbar_callback); |
| |
| hscrollbar_.value(0, ww, 0, 1); |
| hscrollbar_.step(8.0); |
| hscrollbar_.show(); |
| hscrollbar_.callback(hscrollbar_callback); |
| hscrollbar_.type(FL_HORIZONTAL); |
| end(); |
| |
| resize(xx, yy, ww, hh); |
| } |
| |
| |
| /** Destroys the Fl_Help_View widget. |
| |
| The destructor destroys the widget and frees all memory that has been |
| allocated for the current document. |
| */ |
| Fl_Help_View::~Fl_Help_View() |
| { |
| clear_selection(); |
| free_data(); |
| } |
| |
| |
| /** Loads the specified file. |
| |
| This method loads the specified file or URL. |
| */ |
| int // O - 0 on success, -1 on error |
| Fl_Help_View::load(const char *f)// I - Filename to load (may also have target) |
| { |
| FILE *fp; // File to read from |
| long len; // Length of file |
| char *target; // Target in file |
| char *slash; // Directory separator |
| const char *localname; // Local filename |
| char error[1024]; // Error buffer |
| char newname[FL_PATH_MAX]; // New filename buffer |
| |
| // printf("load(%s)\n",f); fflush(stdout); |
| |
| if (strncmp(f, "ftp:", 4) == 0 || |
| strncmp(f, "http:", 5) == 0 || |
| strncmp(f, "https:", 6) == 0 || |
| strncmp(f, "ipp:", 4) == 0 || |
| strncmp(f, "mailto:", 7) == 0 || |
| strncmp(f, "news:", 5) == 0) { |
| char urimsg[FL_PATH_MAX]; |
| if ( fl_open_uri(f, urimsg, sizeof(urimsg)) == 0 ) { |
| clear_selection(); |
| |
| strlcpy(newname, f, sizeof(newname)); |
| if ((target = strrchr(newname, '#')) != NULL) |
| *target++ = '\0'; |
| |
| if (link_) |
| localname = (*link_)(this, newname); |
| else |
| localname = filename_; |
| |
| if (!localname) |
| return (0); |
| |
| free_data(); |
| |
| strlcpy(filename_, newname, sizeof(filename_)); |
| strlcpy(directory_, newname, sizeof(directory_)); |
| |
| // Note: We do not support Windows backslashes, since they are illegal |
| // in URLs... |
| if ((slash = strrchr(directory_, '/')) == NULL) |
| directory_[0] = '\0'; |
| else if (slash > directory_ && slash[-1] != '/') |
| *slash = '\0'; |
| |
| snprintf(error, sizeof(error), |
| "<HTML><HEAD><TITLE>Error</TITLE></HEAD>" |
| "<BODY><H1>Error</H1>" |
| "<P>Unable to follow the link \"%s\" - " |
| "%s.</P></BODY>", |
| f, urimsg); |
| value(error); |
| //return(-1); |
| } |
| return(0); |
| } |
| |
| clear_selection(); |
| |
| strlcpy(newname, f, sizeof(newname)); |
| if ((target = strrchr(newname, '#')) != NULL) |
| *target++ = '\0'; |
| |
| if (link_) |
| localname = (*link_)(this, newname); |
| else |
| localname = filename_; |
| |
| if (!localname) |
| return (0); |
| |
| free_data(); |
| |
| strlcpy(filename_, newname, sizeof(filename_)); |
| strlcpy(directory_, newname, sizeof(directory_)); |
| |
| // Note: We do not support Windows backslashes, since they are illegal |
| // in URLs... |
| if ((slash = strrchr(directory_, '/')) == NULL) |
| directory_[0] = '\0'; |
| else if (slash > directory_ && slash[-1] != '/') |
| *slash = '\0'; |
| |
| if (strncmp(localname, "file:", 5) == 0) |
| localname += 5; // Adjust for local filename... |
| |
| if ((fp = fl_fopen(localname, "rb")) != NULL) |
| { |
| fseek(fp, 0, SEEK_END); |
| len = ftell(fp); |
| rewind(fp); |
| |
| value_ = (const char *)calloc(len + 1, 1); |
| if (fread((void *)value_, 1, len, fp)==0) { /* use default 0 */ } |
| fclose(fp); |
| } |
| else |
| { |
| snprintf(error, sizeof(error), |
| "<HTML><HEAD><TITLE>Error</TITLE></HEAD>" |
| "<BODY><H1>Error</H1>" |
| "<P>Unable to follow the link \"%s\" - " |
| "%s.</P></BODY>", |
| localname, strerror(errno)); |
| value_ = strdup(error); |
| } |
| |
| initial_load = 1; |
| format(); |
| initial_load = 0; |
| |
| if (target) |
| topline(target); |
| else |
| topline(0); |
| |
| return (0); |
| } |
| |
| |
| /** Resizes the help widget. */ |
| |
| void |
| Fl_Help_View::resize(int xx, // I - New left position |
| int yy, // I - New top position |
| int ww, // I - New width |
| int hh) // I - New height |
| { |
| Fl_Boxtype b = box() ? box() : FL_DOWN_BOX; |
| // Box to draw... |
| |
| |
| Fl_Widget::resize(xx, yy, ww, hh); |
| |
| int scrollsize = scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size(); |
| scrollbar_.resize(x() + w() - scrollsize - Fl::box_dw(b) + Fl::box_dx(b), |
| y() + Fl::box_dy(b), scrollsize, h() - scrollsize - Fl::box_dh(b)); |
| hscrollbar_.resize(x() + Fl::box_dx(b), |
| y() + h() - scrollsize - Fl::box_dh(b) + Fl::box_dy(b), |
| w() - scrollsize - Fl::box_dw(b), scrollsize); |
| |
| format(); |
| } |
| |
| |
| /** Scrolls the text to the indicated position, given a named destination. |
| |
| \param[in] n target name |
| */ |
| void |
| Fl_Help_View::topline(const char *n) // I - Target name |
| { |
| Fl_Help_Target key, // Target name key |
| *target; // Pointer to matching target |
| |
| |
| if (ntargets_ == 0) |
| return; |
| |
| strlcpy(key.name, n, sizeof(key.name)); |
| |
| target = (Fl_Help_Target *)bsearch(&key, targets_, ntargets_, sizeof(Fl_Help_Target), |
| (compare_func_t)compare_targets); |
| |
| if (target != NULL) |
| topline(target->y); |
| } |
| |
| |
| /** Scrolls the text to the indicated position, given a pixel line. |
| |
| If the given pixel value \p top is out of range, then the text is |
| scrolled to the top or bottom of the document, resp. |
| |
| \param[in] top top line number in pixels (0 = start of document) |
| */ |
| void |
| Fl_Help_View::topline(int top) // I - Top line number |
| { |
| if (!value_) |
| return; |
| |
| int scrollsize = scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size(); |
| if (size_ < (h() - scrollsize) || top < 0) |
| top = 0; |
| else if (top > size_) |
| top = size_; |
| |
| topline_ = top; |
| |
| scrollbar_.value(topline_, h() - scrollsize, 0, size_); |
| |
| do_callback(); |
| |
| redraw(); |
| } |
| |
| |
| /** Scrolls the text to the indicated position, given a pixel column. |
| |
| If the given pixel value \p left is out of range, then the text is |
| scrolled to the left or right side of the document, resp. |
| |
| \param[in] left left column number in pixels (0 = left side) |
| */ |
| void |
| Fl_Help_View::leftline(int left) // I - Left position |
| { |
| if (!value_) |
| return; |
| |
| int scrollsize = scrollbar_size_ ? scrollbar_size_ : Fl::scrollbar_size(); |
| if (hsize_ < (w() - scrollsize) || left < 0) |
| left = 0; |
| else if (left > hsize_) |
| left = hsize_; |
| |
| leftline_ = left; |
| |
| hscrollbar_.value(leftline_, w() - scrollsize, 0, hsize_); |
| |
| redraw(); |
| } |
| |
| |
| /** Sets the current help text buffer to the string provided and reformats the text. |
| |
| The provided character string \p val is copied internally and will be |
| freed when value() is called again, or when the widget is destroyed. |
| |
| If \p val is NULL, then the widget is cleared. |
| */ |
| void |
| Fl_Help_View::value(const char *val) // I - Text to view |
| { |
| clear_selection(); |
| free_data(); |
| set_changed(); |
| |
| if (!val) |
| return; |
| |
| value_ = strdup(val); |
| |
| initial_load = 1; |
| format(); |
| initial_load = 0; |
| |
| topline(0); |
| leftline(0); |
| } |
| |
| |
| #ifdef ENC |
| # undef ENC |
| #endif |
| // part b in the table seems to be mac_roman - beku |
| # define ENC(a, b) a |
| |
| |
| /** Returns the character code associated with a quoted char. */ |
| static int // O - Code or -1 on error |
| quote_char(const char *p) { // I - Quoted string |
| int i; // Looping var |
| static struct { |
| const char *name; |
| int namelen; |
| int code; |
| } *nameptr, // Pointer into name array |
| names[] = { // Quoting names |
| { "Aacute;", 7, ENC(193,231) }, |
| { "aacute;", 7, ENC(225,135) }, |
| { "Acirc;", 6, ENC(194,229) }, |
| { "acirc;", 6, ENC(226,137) }, |
| { "acute;", 6, ENC(180,171) }, |
| { "AElig;", 6, ENC(198,174) }, |
| { "aelig;", 6, ENC(230,190) }, |
| { "Agrave;", 7, ENC(192,203) }, |
| { "agrave;", 7, ENC(224,136) }, |
| { "amp;", 4, ENC('&','&') }, |
| { "Aring;", 6, ENC(197,129) }, |
| { "aring;", 6, ENC(229,140) }, |
| { "Atilde;", 7, ENC(195,204) }, |
| { "atilde;", 7, ENC(227,139) }, |
| { "Auml;", 5, ENC(196,128) }, |
| { "auml;", 5, ENC(228,138) }, |
| { "brvbar;", 7, ENC(166, -1) }, |
| { "bull;", 5, ENC(149,165) }, |
| { "Ccedil;", 7, ENC(199,199) }, |
| { "ccedil;", 7, ENC(231,141) }, |
| { "cedil;", 6, ENC(184,252) }, |
| { "cent;", 5, ENC(162,162) }, |
| { "copy;", 5, ENC(169,169) }, |
| { "curren;", 7, ENC(164, -1) }, |
| { "deg;", 4, ENC(176,161) }, |
| { "divide;", 7, ENC(247,214) }, |
| { "Eacute;", 7, ENC(201,131) }, |
| { "eacute;", 7, ENC(233,142) }, |
| { "Ecirc;", 6, ENC(202,230) }, |
| { "ecirc;", 6, ENC(234,144) }, |
| { "Egrave;", 7, ENC(200,233) }, |
| { "egrave;", 7, ENC(232,143) }, |
| { "ETH;", 4, ENC(208, -1) }, |
| { "eth;", 4, ENC(240, -1) }, |
| { "Euml;", 5, ENC(203,232) }, |
| { "euml;", 5, ENC(235,145) }, |
| { "euro;", 5, ENC(128,219) }, |
| { "frac12;", 7, ENC(189, -1) }, |
| { "frac14;", 7, ENC(188, -1) }, |
| { "frac34;", 7, ENC(190, -1) }, |
| { "gt;", 3, ENC('>','>') }, |
| { "Iacute;", 7, ENC(205,234) }, |
| { "iacute;", 7, ENC(237,146) }, |
| { "Icirc;", 6, ENC(206,235) }, |
| { "icirc;", 6, ENC(238,148) }, |
| { "iexcl;", 6, ENC(161,193) }, |
| { "Igrave;", 7, ENC(204,237) }, |
| { "igrave;", 7, ENC(236,147) }, |
| { "iquest;", 7, ENC(191,192) }, |
| { "Iuml;", 5, ENC(207,236) }, |
| { "iuml;", 5, ENC(239,149) }, |
| { "laquo;", 6, ENC(171,199) }, |
| { "lt;", 3, ENC('<','<') }, |
| { "macr;", 5, ENC(175,248) }, |
| { "micro;", 6, ENC(181,181) }, |
| { "middot;", 7, ENC(183,225) }, |
| { "nbsp;", 5, ENC(' ',' ') }, |
| { "not;", 4, ENC(172,194) }, |
| { "Ntilde;", 7, ENC(209,132) }, |
| { "ntilde;", 7, ENC(241,150) }, |
| { "Oacute;", 7, ENC(211,238) }, |
| { "oacute;", 7, ENC(243,151) }, |
| { "Ocirc;", 6, ENC(212,239) }, |
| { "ocirc;", 6, ENC(244,153) }, |
| { "Ograve;", 7, ENC(210,241) }, |
| { "ograve;", 7, ENC(242,152) }, |
| { "ordf;", 5, ENC(170,187) }, |
| { "ordm;", 5, ENC(186,188) }, |
| { "Oslash;", 7, ENC(216,175) }, |
| { "oslash;", 7, ENC(248,191) }, |
| { "Otilde;", 7, ENC(213,205) }, |
| { "otilde;", 7, ENC(245,155) }, |
| { "Ouml;", 5, ENC(214,133) }, |
| { "ouml;", 5, ENC(246,154) }, |
| { "para;", 5, ENC(182,166) }, |
| { "premil;", 7, ENC(137,228) }, |
| { "plusmn;", 7, ENC(177,177) }, |
| { "pound;", 6, ENC(163,163) }, |
| { "quot;", 5, ENC('\"','\"') }, |
| { "raquo;", 6, ENC(187,200) }, |
| { "reg;", 4, ENC(174,168) }, |
| { "sect;", 5, ENC(167,164) }, |
| { "shy;", 4, ENC(173,'-') }, |
| { "sup1;", 5, ENC(185, -1) }, |
| { "sup2;", 5, ENC(178, -1) }, |
| { "sup3;", 5, ENC(179, -1) }, |
| { "szlig;", 6, ENC(223,167) }, |
| { "THORN;", 6, ENC(222, -1) }, |
| { "thorn;", 6, ENC(254, -1) }, |
| { "times;", 6, ENC(215,'x') }, |
| { "trade;", 6, ENC(153,170) }, |
| { "Uacute;", 7, ENC(218,242) }, |
| { "uacute;", 7, ENC(250,156) }, |
| { "Ucirc;", 6, ENC(219,243) }, |
| { "ucirc;", 6, ENC(251,158) }, |
| { "Ugrave;", 7, ENC(217,244) }, |
| { "ugrave;", 7, ENC(249,157) }, |
| { "uml;", 4, ENC(168,172) }, |
| { "Uuml;", 5, ENC(220,134) }, |
| { "uuml;", 5, ENC(252,159) }, |
| { "Yacute;", 7, ENC(221, -1) }, |
| { "yacute;", 7, ENC(253, -1) }, |
| { "yen;", 4, ENC(165,180) }, |
| { "Yuml;", 5, ENC(159,217) }, |
| { "yuml;", 5, ENC(255,216) } |
| }; |
| |
| if (!strchr(p, ';')) return -1; |
| if (*p == '#') { |
| if (*(p+1) == 'x' || *(p+1) == 'X') return strtol(p+2, NULL, 16); |
| else return atoi(p+1); |
| } |
| for (i = (int)(sizeof(names) / sizeof(names[0])), nameptr = names; i > 0; i --, nameptr ++) |
| if (strncmp(p, nameptr->name, nameptr->namelen) == 0) |
| return nameptr->code; |
| |
| return -1; |
| } |
| |
| |
| /** The vertical scrollbar callback. */ |
| static void |
| scrollbar_callback(Fl_Widget *s, void *) |
| { |
| ((Fl_Help_View *)(s->parent()))->topline(int(((Fl_Scrollbar*)s)->value())); |
| } |
| |
| |
| /** The horizontal scrollbar callback. */ |
| static void |
| hscrollbar_callback(Fl_Widget *s, void *) |
| { |
| ((Fl_Help_View *)(s->parent()))->leftline(int(((Fl_Scrollbar*)s)->value())); |
| } |
| |
| |
| // |
| // End of "$Id: Fl_Help_View.cxx 8063 2010-12-19 21:20:10Z matt $". |
| // |