/* Copyright 1997 Acorn Computers Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /***************************************************/ /* File : Redraw.c */ /* */ /* Purpose: Redraw functions for the browser. */ /* */ /* Author : A.D.Hodgkinson */ /* */ /* History: 29-Nov-96: Created. */ /***************************************************/ #include <stdlib.h> #include <stdio.h> #include <string.h> #include "swis.h" #include "flex.h" #include "HTMLLib.h" /* HTML library API, Which will include html2_ext.h, tags.h and struct.h */ #include "wimp.h" #include "wimplib.h" #include "event.h" #include "svcprint.h" #include "Global.h" #include "FromROSLib.h" #include "MiscDefs.h" #include "Utils.h" #include "Browser.h" #include "ChoiceDefs.h" #include "CSIM.h" #include "Fetch.h" /* (Which itself includes URLstat.h) */ #include "FontManage.h" #include "Forms.h" #include "Frames.h" #include "Images.h" #include "Object.h" #include "Printing.h" /* Only for the PrintSplitFraction definition at present */ #include "PrintStyle.h" #include "Reformat.h" #include "Tables.h" #include "TokenUtils.h" #include "Toolbars.h" #ifdef UNIFONT #include "Unicode/iso10646.h" #include "Unifont.h" #endif #include "Redraw.h" /* Static function prototypes */ static void redraw_input_field (browser_data * b, HStream * t, BBox * box, int colour, int menu); static void redraw_button (browser_data * b, HStream * t, BBox * box, int in); static void redraw_switch (browser_data * b, HStream * t, int x, int y, char * spr, WimpRedrawWindowBlock * r); static void redraw_bullet (int x, int y, int bullet, WimpRedrawWindowBlock * r); /* Used for printing */ static int use_noback = 0; /* Internal recursive functions. These do the actual work that their */ /* similarly named and oft externally visible counterparts claim to */ /* do, but are part of the recursive code needed for e.g. tables. */ static _kernel_oserror * redraw_draw_r (int toplevel, int xorg, int yorg, browser_data * b, reformat_cell * d, WimpRedrawWindowBlock * r, int noback, HStream * nocontent); /*************************************************/ /* redraw_header() */ /* */ /* Returns the header type (<H1>, <H2> etc. as */ /* a number from 1 - 7) extracted from the flags */ /* bits of an HStream structure. */ /* */ /* Parameters: The flags word. */ /*************************************************/ int redraw_header(unsigned int flags) { /* H_MASK and H_SHIFT are defined in HTMLLib:tags.h */ flags &= H_MASK; flags = (flags >> H_SHIFT); return flags; } /*************************************************/ /* redraw_backcol() */ /* */ /* Small function to return the actual */ /* background colour of a browser window. */ /* */ /* Parameters: A pointer to a browser_data */ /* structure associated with the */ /* window in question. */ /*************************************************/ int redraw_backcol(browser_data * b) { /* If the background colour isn't set or the Choices say */ /* to override document colours, return the default; else */ /* return the document-specified background colour. */ #ifdef TRACE if (tl & (1u<<9)) Printf("redraw_backcol: Called with choices.background_colour = %p\n",(void *) choices.background_colour); #endif return (((b->background_colour == -1) || (!b->use_source_cols)) ? (choices.background_colour) : (b->background_colour)); } /*************************************************/ /* redraw_background_colour() */ /* */ /* Returns a background colour hint for text of */ /* a given foreground colour. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* holding the background info; */ /* */ /* A foreground colour (as a palette */ /* entry, for more details see */ /* redraw_set_colour()). */ /* */ /* Returns: A background colour. */ /*************************************************/ int redraw_background_colour(browser_data * b, int foregroundcolour) { if (!b->show_background) return redraw_backcol(b); switch (b->antialias_colour) { /* Defeat anti-aliasing by giving the same background colour */ /* as the foreground if antialias_colour is -1. */ case -1: return foregroundcolour; /* If antialias_colour is -2, return the background colour from the */ /* browser_data struct unless this is -1, in which case return the */ /* foreground colour again. */ case -2: return (b->background_colour == -1 ? foregroundcolour : b->background_colour); } /* Return either the default background colour or the anti-alias */ /* colour, depending on whether document colour overriding is */ /* on or off respectively. */ return (!b->use_source_cols ? choices.background_colour : b->antialias_colour); } /*************************************************/ /* redraw_token_colour() */ /* */ /* Returns the colour to plot a token in, on the */ /* assumption that it contains some sort of text */ /* */ /* Parameters: Pointer to a browser_data struct */ /* with details of the token stream */ /* within it; */ /* */ /* Pointer to the token. */ /* */ /* Returns: The colour to plot in, as a */ /* palette entry (see */ /* redraw_set_colour()). */ /*************************************************/ int redraw_token_colour(browser_data * b, HStream * t) { if (t->tagno == TAG_INPUT || t->tagno == TAG_TEXTAREA || t->tagno == TAG_SELECT) return 0; /* If we're printing, see if the Print Style dictates that any */ /* text should be black. */ if (printing) { if (printstyle_always_use_black()) return Redraw_Colour_Black; if (printstyle_black_no_background() && use_noback) return Redraw_Colour_Black; } /* If the token represents a link, use different colours according */ /* to the state of that link (followed, unfollowed etc). */ if (ISLINK(t)) { /* If tokens are highlighted, return the appropriate colour */ if (b->highlight) return (b->use_source_cols ? b->followed_colour : choices.followed_colour); /* If tokens are selected, return the appropriate colour */ if (redraw_selected(b, t)) return (b->use_source_cols ? b->selected_colour : choices.selected_colour); /* If the token has been followed in the past, give the used colour */ /* - otherwise give the unfollowed link colour. */ if (!printing && (t->flags & HFlags_LinkVisited)) return (b->use_source_cols ? b->used_colour : choices.used_colour); return (b->use_source_cols ? b->link_colour : choices.link_colour); } /* If the token has attached specific colour information, return that */ if ((t->type & TYPE_COLOURED) && b->use_source_cols) return (t->colour << 8); /* If the token is just text, return the normal text colour */ return (b->use_source_cols ? b->text_colour : choices.text_colour); } /*************************************************/ /* redraw_set_colour() */ /* */ /* Sets the foreground colour for future plots. */ /* */ /* Parameters: A 32-bit colour number in the */ /* form BBGGRRcc where cc = GCOL, */ /* or BBGGRR are blue, green and */ /* red components. */ /*************************************************/ void redraw_set_colour(int colour) { #ifdef TRACE if (tl & (1u<<9)) Printf("redraw_set_colour: Called with colour = %p\n",(void *) colour); #endif /* Don't use dithering if anti-twittering redraws */ #ifdef ANTI_TWITTER _swix(ColourTrans_SetGCOL, _IN(0) | _INR(3,4), colour, /* Colour to change to */ 0, /* No dithering */ 0); /* GCOL action 0 */ #else _swix(ColourTrans_SetGCOL, _IN(0) | _INR(3,4), colour, /* Colour to change to */ 1<<8, /* Use ECFs (dithering) for better representation */ 0); /* GCOL action 0 */ #endif } /*************************************************/ /* redraw_display_width() */ /* */ /* Returns the available display width for a */ /* given browser redraw cell, in OS units. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the cell; */ /* */ /* Pointer to a reformat_cell struct */ /* representing the redraw cell. */ /* */ /* Returns: The display width, in OS units. */ /*************************************************/ int redraw_display_width(browser_data * b, reformat_cell * d) { if (!d || !d->table) return b->display_width; else { int osw; convert_to_os(d->cellwidth, &osw); return osw; } } /*************************************************/ /* redraw_display_height() */ /* */ /* Returns the available display height for a */ /* given browser redraw cell, in OS units; for a */ /* base browser window, it will subtract the */ /* toolbar heights as required. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the cell; */ /* */ /* Pointer to a reformat_cell struct */ /* representing the redraw cell. */ /* */ /* Returns: The display width, in OS units. */ /*************************************************/ int redraw_display_height(browser_data * b, reformat_cell * d) { if (!d || !d->table) { /* Subtract a bit for aesthetics and to account for the */ /* amount a line might naturally be overheight, so that */ /* (say) images scaled to 100% height don't lead to a */ /* vertically scrollable page. */ return b->display_height - b->leading * 3; } else { int osh; convert_to_os(d->cellheight, &osh); return osh; } } /*************************************************/ /* redraw_left_margin() */ /* */ /* Returns the left hand margin width for a */ /* given browser redraw cell, in millipoints. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the cell; */ /* */ /* Pointer to a reformat_cell struct */ /* representing the redraw cell. */ /* */ /* Returns: The left hand margin, in */ /* millipoints. */ /*************************************************/ int redraw_left_margin(browser_data * b, reformat_cell * d) { if (!d || !d->table) return b->left_margin; else { /* Left margin -> cellpadding for a table cell */ int cellpadmp = d->table->cellpadding * 2; /* 1 'web pixel' = 2 OS units */ convert_to_points(cellpadmp, &cellpadmp); return cellpadmp; } } /*************************************************/ /* redraw_right_margin() */ /* */ /* Returns the right hand margin width for a */ /* given browser redraw cell, in millipoints. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the cell; */ /* */ /* Pointer to a reformat_cell struct */ /* representing the redraw cell. */ /* */ /* Returns: The right hand margin, in */ /* millipoints. */ /*************************************************/ int redraw_right_margin(browser_data * b, reformat_cell * d) { if (!d || !d->table) return b->right_margin; else { /* Left margin -> cellpadding for a table cell */ int cellpadmp = d->table->cellpadding * 2; /* 1 'web pixel' = 2 OS units */ convert_to_points(cellpadmp, &cellpadmp); return cellpadmp; } // else return 0; /* No margin on table cells - cellpadding/spacing handled separately by reformatter */ } /*************************************************/ /* redraw_left_gap() */ /* */ /* Works out the left hand indented margin for a */ /* given browser redraw cell, in millipoints. */ /* This will be redraw_left_margin plus a value */ /* dependent upon the given token (to allow e.g. */ /* list items to be indented). */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the cell; */ /* */ /* Pointer to a reformat_cell struct */ /* representing the redraw cell; */ /* */ /* Pointer to a token holding */ /* indentation information. */ /* */ /* Returns: The left hand margin, taking */ /* account of list indentations */ /* etc., in millipoints. */ /*************************************************/ int redraw_left_gap(browser_data * b, reformat_cell * d, HStream * t) { int s, i; s = t->style; i = t->indent * b->left_indent; /* Play about outdenting bullets and numbered list items. */ if (t->tagno == TAG_LI) { if (t->text) { _kernel_oserror * e; int h, width, bytes; /* We need to right-align the text. Find out its width... */ h = fm_find_token_font(b, t, 0); e = fm_get_string_width(h, t->text, 0x40000000, 0x40000000, -1, &bytes, &width); if (!e) i -= width; } else { int bullet_width; /* Outdent the bullet */ convert_to_points(reformat_bullet_width(t->indent), &bullet_width); i -= bullet_width; } } /* Add an amount for block quote or address text */ if (s & (BLOCKQUOTE | ADDRESS)) i += b->quote_margin; /* Return the calculated left indent plus the left */ /* margin value. */ i += redraw_left_margin(b, d); return i > 0 ? i : 0; } /*************************************************/ /* redraw_right_gap() */ /* */ /* As redraw_left_gap, but for the right hand */ /* edge of a given redraw browser cell. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the cell; */ /* */ /* Pointer to a reformat_cell struct */ /* representing the redraw cell; */ /* */ /* Pointer to a token holding */ /* indentation information. */ /* */ /* Returns: The right hand margin, taking */ /* account of block quote indents */ /* etc., in millipoints. */ /*************************************************/ int redraw_right_gap(browser_data * b, reformat_cell * d, HStream * t) { int i = 0; /* Add an amount for block quote text */ if (t->style & BLOCKQUOTE) i += b->quote_margin; /* Return the calculated right indent plus the right */ /* margin value. */ i += redraw_right_margin(b, d); return i > 0 ? i : 0; } /*************************************************/ /* redraw_start_x() */ /* */ /* Examines current token and line structure */ /* information within a redraw cell to return an */ /* indent from the left edge of the page at */ /* which something should be drawn - handles */ /* centre and right aligning of lines. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the cell; */ /* */ /* Pointer to a reformat_cell struct */ /* representing the redraw cell; */ /* */ /* Pointer to an HStream (token) */ /* so that a margin can be found; */ /* */ /* Line number which the x offset */ /* needs to be found for. */ /* */ /* Returns: The x offset to plot at, in OS */ /* units. */ /*************************************************/ int redraw_start_x(browser_data * b, reformat_cell * cell, HStream * t, int line) { int x, cn; int align = 0; if (!cell) cell = b->cell; cn = cell->ldata[line].chunks; /* If the token isn't centred just return the margin value; */ /* else work out centre or right alignment indentation. */ if ((t->style & CENTER) || (t->type & TYPE_ALIGN_MASK) == TYPE_CENTRE) align = 1; else if ((t->style & RIGHT) || (t->type & TYPE_ALIGN_MASK) == TYPE_RIGHT) align = 2; if (align) { int i; int left = redraw_left_gap (b, cell, t); int right = redraw_right_gap(b, cell, t); /* Get the window's display width in millipoints */ convert_to_points(redraw_display_width(b, cell), &x); /* Subtract the width of each chunk from this value */ for (i = 0; i < cell->ldata[line].n; x -= cell->cdata[cn].w, i++, cn++); /* Subtract the right hand gap value */ x -= right; /* For centred objects, center between the margins */ if (align == 1) x = left + ((x - left) / 2); /* Sanity check */ if (x < left) x = left; /* Convert back to OS units */ convert_to_os(x, &x); return x; } convert_to_os(redraw_left_gap(b, cell, t), &x); return x; } /*************************************************/ /* redraw_token_x() */ /* */ /* Examines current token and line structure */ /* information within a redraw cell to return an */ /* indent from the left edge of the page at */ /* which a specific token should be drawn. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the cell; */ /* */ /* Pointer to a reformat_cell struct */ /* representing the redraw cell; */ /* */ /* Pointer to the token; */ /* */ /* Line number which the token lies */ /* in; */ /* */ /* Returns: The x offset from the left of the */ /* page that the token starts at, in */ /* OS units. */ /*************************************************/ int redraw_token_x(browser_data * b, reformat_cell * cell, HStream * t, int line) { int x, chunk, mchunk; if (!cell) cell = b->cell; /* Find the starting left hand edge */ convert_to_points(redraw_start_x(b, cell, cell->cdata[cell->ldata[line].chunks].t, line), &x); /* Add up chunk widths */ chunk = cell->ldata[line].chunks; mchunk = cell->ldata[line].n + chunk; while ( chunk < mchunk && cell->cdata[chunk].t != t ) x += cell->cdata[chunk].w, chunk++; convert_to_os(x, &x); /* Return the total */ return x; } /*************************************************/ /* redraw_chunk_x() */ /* */ /* Examines current token and line structure */ /* information within a redraw cell to return an */ /* indent from the left edge of the page at */ /* which a specific chunk should be drawn (i.e. */ /* as redraw_token_x, but you supply a chunk */ /* number rather than a token). */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the cell; */ /* */ /* Pointer to a reformat_cell struct */ /* representing the redraw cell; */ /* */ /* The chunk number; */ /* */ /* Line number the chunk lies in. */ /* */ /* Returns: The x offset from the left of the */ /* page that the chunk starts at, in */ /* OS units. */ /* */ /* Assumes: That the given line does indeed */ /* include the given chunk. */ /*************************************************/ int redraw_chunk_x(browser_data * b, reformat_cell * cell, int chunk, int line) { int x, cchunk, mchunk; if (!cell) cell = b->cell; /* Find the starting left hand edge */ convert_to_points(redraw_start_x(b, cell, cell->cdata[cell->ldata[line].chunks].t, line), &x); /* Add up chunk widths */ cchunk = cell->ldata[line].chunks; mchunk = cchunk + cell->ldata[line].n; while ( cchunk < mchunk && cchunk < chunk ) x += cell->cdata[cchunk].w, cchunk++; convert_to_os(x, &x); /* Return the total */ return x; } /*************************************************/ /* redraw_selected() */ /* */ /* Looks at the 'selected' field for the given */ /* browser_data struct, and returns 1 if the */ /* given token should be part of the selection */ /* that 'selected' lies in. */ /* */ /* This is for whole token selection, e.g. when */ /* keyboard navigating a page - it isn't part of */ /* a more general mouse-driven text selection */ /* model. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the selection; */ /* Pointer to the token to compare. */ /* */ /* Returns: 1 if the token is part of the */ /* selection, else 0. */ /*************************************************/ int redraw_selected(browser_data * b, HStream * token) { HStream * top; HStream * end; browser_data * owner; browser_data * ancestor = utils_ancestor(b); int found = 0; /* If printing, don't want to show anything as selected */ if (printing) return 0; /* Otherwise, find out if the token is part of a selection. */ owner = ancestor->selected_owner; if (!ancestor->selected) return 0; if (ancestor->selected == token) return 1; tokenutils_anchor_range(owner, ancestor->selected, &top, &end); if (top && end) { do { if (token == top) found = 1; else top = top->next; } while (top && top != end->next && !found); } return found; } /*************************************************/ /* redraw_border_around_box() */ /* */ /* Draws a 2 pixel thick border around a given */ /* bounding box, in a given colour. */ /* */ /* Parameters: Pointer to the BBox; */ /* */ /* Colour to use, as a palette entry */ /* (for more details see */ /* redraw_set_colour()). */ /*************************************************/ void redraw_border_around_box(BBox * rbox, int colour) { BBox box; box = *rbox; box.xmin &= ~(wimpt_dx() - 1); box.ymin &= ~(wimpt_dy() - 1); box.xmax &= ~(wimpt_dx() - 1); box.ymax &= ~(wimpt_dy() - 1); redraw_set_colour(colour); bbc_rectangle(box.xmin - 4, box.ymin - 4, box.xmax - box.xmin + 7, box.ymax - box.ymin + 7); bbc_rectangle(box.xmin - 2, box.ymin - 2, box.xmax - box.xmin + 3, box.ymax - box.ymin + 3); } /*************************************************/ /* redraw_input_field() */ /* */ /* For forms, redraws an input field element. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the element; */ /* */ /* Pointer to the token representing */ /* this element; */ /* */ /* BBox of the field, in window */ /* coords (and thus OS units); */ /* */ /* Border colour (as a palette */ /* entry, for more details see */ /* redraw_set_colour()); */ /* */ /* 1 if this is a display field, */ /* i.e. it has a menu icon, and the */ /* border colour is ignored; else 0. */ /*************************************************/ static void redraw_input_field(browser_data * b, HStream * t, BBox * box, int colour, int menu) { int w, h; w = box->xmax - box->xmin - 1; h = box->ymax - box->ymin - 1; if (menu) { int sw; BBox shorter; shorter = *box; if (read_sprite_size("fgright", &sw, NULL)) sw = 44; shorter.xmax -= (sw + 8); if (shorter.xmax < shorter.xmin) shorter.xmax = shorter.xmin + sw; /* Redraw the display region as a slabbed button */ redraw_button(b, t, &shorter, 2); } else { /* Redraw the inside in white */ redraw_set_colour(Redraw_Colour_White); bbc_rectanglefill(box->xmin, box->ymin, w, h); /* Redraw the border */ redraw_set_colour(colour); bbc_rectanglefill(box->xmin, box->ymin, 3, h); bbc_rectanglefill(box->xmax - 4, box->ymin, 3, h); bbc_rectanglefill(box->xmin + 4, box->ymin, w - 8, 3); bbc_rectanglefill(box->xmin + 4, box->ymax - 4, w - 8, 3); /* Draw a wider border if selected */ if (redraw_selected(b, t)) redraw_border_around_box(box, b->selected_colour); } } /*************************************************/ /* redraw_button() */ /* */ /* For forms, redraws a button element. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the element; */ /* */ /* Pointer to the token representing */ /* this element; */ /* */ /* BBox of the field, in window */ /* coords (and thus OS units); */ /* */ /* 1 to be slabbed in, 2 to be */ /* slabbed in but with a light grey */ /* background rather than dark, else */ /* 0. */ /*************************************************/ static void redraw_button(browser_data * b, HStream * t, BBox * box, int in) { int w, h; w = box->xmax - box->xmin - 1; h = box->ymax - box->ymin - 1; redraw_set_colour((in == 1) ? Redraw_Colour_MidGrey : Redraw_Colour_BackGrey); bbc_rectanglefill(box->xmin, box->ymin, w, h); redraw_set_colour(in ? Redraw_Colour_PlinthGrey : Redraw_Colour_AlmostWhite); bbc_rectanglefill(box->xmin, box->ymin, 1, h); bbc_rectanglefill(box->xmin + 2, box->ymin + 2, 1, h - 2); bbc_rectanglefill(box->xmin + 4, box->ymax - 2, w - 4, 1); bbc_rectanglefill(box->xmin + 4, box->ymax - 4, w - 6, 1); redraw_set_colour(in ? Redraw_Colour_AlmostWhite : Redraw_Colour_PlinthGrey); bbc_rectanglefill(box->xmin + 2, box->ymin, w - 2, 1); bbc_rectanglefill(box->xmin + 4, box->ymin + 2, w - 4, 1); bbc_rectanglefill(box->xmax - 4, box->ymin + 4, 1, h - 8); bbc_rectanglefill(box->xmax - 2, box->ymin + 4, 1, h - 6); if (redraw_selected(b, t)) redraw_border_around_box(box, b->selected_colour); } /*************************************************/ /* redraw_switch() */ /* */ /* For forms, redraws a switch (radio or option) */ /* element. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the element; */ /* */ /* Pointer to the token representing */ /* this element; */ /* */ /* x coordinate (points, in screen */ /* coords) of left hand edge; */ /* */ /* y coordinate (points, in screen */ /* coords) of right hand edge; */ /* */ /* Pointer to sprite name to use; */ /* */ /* A WimpRedrawWindowBlock pointer, */ /* if in a redraw loop (can be NULL */ /* if not in a redraw loop). */ /*************************************************/ static void redraw_switch(browser_data * b, HStream * t, int x, int y, char * spr, WimpRedrawWindowBlock * r) { int ox, oy, w, h; WimpPlotIconBlock block; BBox icon; convert_pair_to_os(x, y, &ox, &oy); oy -= 8; if (read_sprite_size(spr, &w, &h)) w = h = 44; icon.xmin = ox; icon.ymin = oy; icon.xmax = icon.xmin + w; icon.ymax = icon.ymin + h; if (r) coords_box_toworkarea(&icon, r); block.bbox = icon; block.flags = 0x1700311A; block.data.is.sprite = spr; block.data.is.sprite_area = (void *) sprite_block; block.data.is.sprite_name_length = strlen(spr); _swix(Wimp_PlotIcon, _IN(1) | _INR(4,5), &block, 0, 0); if (redraw_selected(b, t)) { if (r) coords_box_toscreen(&icon, r); redraw_border_around_box(&icon, b->selected_colour); } } /*************************************************/ /* redraw_bullet() */ /* */ /* Redraws a bullet point. */ /* */ /* Parameters: x coordinate (points, in screen */ /* coords) of left hand edge; */ /* */ /* y coordinate (points, in screen */ /* coords) of right hand edge; */ /* */ /* The bullet number; */ /* */ /* A WimpRedrawWindowBlock pointer, */ /* if in a redraw loop (can be NULL */ /* if not in a redraw loop). */ /*************************************************/ static void redraw_bullet(int x, int y, int bullet, WimpRedrawWindowBlock * r) { char spr[32]; int w, h; BBox icon; WimpPlotIconBlock block; sprintf(spr, "b%d", (bullet + bullets - 1) % bullets); if (read_sprite_size(spr, &w, &h)) w = h = 32; icon.xmin = x; icon.ymin = y; icon.xmax = icon.xmin + w; icon.ymax = icon.ymin + h; if (r) coords_box_toworkarea(&icon, r); block.bbox = icon; block.flags = 0x1700311A; /* 11a */ block.data.is.sprite = spr; block.data.is.sprite_area = (void *) sprite_block; block.data.is.sprite_name_length = strlen(spr); _swix(Wimp_PlotIcon, _IN(1) | _INR(4,5), &block, 0, 0); } /*************************************************/ /* redraw_draw_placeholder() */ /* */ /* Redraws a slabbed in place holder (unless the */ /* item is very small, in which case just at */ /* thin black border is plotted) for a given */ /* token, with optional text inside. */ /* */ /* Parameters: A pointer to a browser_data */ /* structure relevant to the redraw; */ /* */ /* A WimpRedrawWindowBlock pointer, */ /* with window area and redraw */ /* rectangle details filled in; */ /* */ /* Pointer to a BBox in which xmin */ /* and ymin hold the screen coords */ /* for the bottom left hand corner, */ /* and xmax and ymax hold the width */ /* and height of the placeholder in */ /* OS units; */ /* */ /* Pointer to the HStream struct the */ /* placeholder is to represent; */ /* */ /* Pointer to a null-terminated */ /* piece of to plot inside, or NULL. */ /*************************************************/ void redraw_draw_placeholder(browser_data * b, WimpRedrawWindowBlock * r, BBox * holder, HStream * token, const char * text) { BBox ph = *holder; // In case we want to adjust it later, e.g. for H/VSPACE /* A slabbed box if the size is great enough */ if (ph.xmax > 8 && ph.ymax > 8) { /* xmin, ymin hold the bottom left hand corner coordinates, whilst */ /* xmax, ymax hold the width and height. The adjustments are to */ /* account for the way the bbc_rectanglefill function works; e.g., */ /* to get a width of 4 OS units, ask for 3 (as it adds this to the */ /* x coordinate and treats it as an inclusive x coordinate max). */ /* There are corrections to plot 2 OS units inside of the real */ /* bounding box (looks better when images touch each other) and to */ /* get the darker sides of the 'slabbed in' box overlapping the */ /* lighter sides by the right amount. */ redraw_set_colour(Redraw_Colour_AlmostWhite); bbc_rectanglefill(ph.xmin + 2, ph.ymin + 2, ph.xmax - 5, 3); bbc_rectanglefill(ph.xmax + ph.xmin - 6, ph.ymin + 2, 3, ph.ymax - 5); redraw_set_colour(Redraw_Colour_MidGrey); bbc_rectanglefill(ph.xmin + 2, ph.ymax + ph.ymin - 6, ph.xmax - 7, 3); bbc_rectanglefill(ph.xmin + 2, ph.ymin + 4, 3, ph.ymax - 7); } /* Otherwise a thin black frame */ else { if (ph.xmax < 2) ph.xmax = 2; if (ph.ymax < 2) ph.ymax = 2; redraw_set_colour(0); bbc_rectangle(ph.xmin,ph.ymin,ph.xmax - 1,ph.ymax - 1); } /* Plot any text that there is */ if (text && *text) { /* Find out the bounding box needed to contain the text */ int h, xpos, vcent, stringwidth, stringheight, size; BBox fbox; BBox * ibox = NULL; fbox.xmin = fbox.ymin = 0; /* Claim the font */ size = (fm_size(token->fontsize) * 80) / 100; h = fm_find_font(b, "sans", size, size, 0, 0); /* Find the string width of the ALT text */ fm_get_string_width(h, text, Reformat_AsWideAsPossible_MP, strlen(text), -1, NULL, &stringwidth); convert_to_os(stringwidth, &stringwidth); /* Find the font height */ fm_font_box(h, &fbox); stringheight = fbox.ymax - fbox.ymin; /* Set xpos to the horizontal offset to plot at. */ /* Remember that 'box' contains the bottom */ /* left coordinates of the image, then the width */ /* and height in OS units in xmax and ymax. */ /* Similarly, centre vertically */ vcent = (ph.ymax - stringheight) / 2 - fbox.ymin; if (vcent <= 0) vcent = 10; vcent += ph.ymin; xpos = (ph.xmax - stringwidth) / 2; if (xpos <= 0) xpos = 10; xpos += ph.xmin; /* Now set the graphics window to the image bounding box, */ /* taking account of the slabbed border already drawn above. */ /* Need to set this to the intersection of the current */ /* graphics window though, or could end up scribbling over */ /* things that aren't meant to be touched. */ fbox.xmin = ph.xmin + 8; fbox.xmax = ph.xmin + ph.xmax - 9; fbox.ymin = ph.ymin + 8; fbox.ymax = ph.ymin + ph.ymax - 9; /* If the max coordinates are less than the min, the image BBox */ /* is too small to fit anything in. Don't proceed, as the */ /* attempt to set the graphics rectangle would fail, default to */ /* the whole screen, and then random bits of ALT text would get */ /* scribbled all over the place... */ if (fbox.xmin < fbox.xmax && fbox.ymin < fbox.ymax) { /* Need to ensure a graphics window is set up for the plot, as */ /* text may be clipped, but this needs to take the current */ /* redraw rectangle into account too - hence the function call. */ ibox = set_graphics_intersection(&fbox, &r->redraw_area); if (ibox) { int colour; colour = redraw_token_colour(b, token); fm_set_font_colour(h, colour, redraw_background_colour(b, colour)); fm_puts(h, xpos, vcent, text, 1, b->background_image >= 0 && b->show_background); /* Underline text if it's a link and the browser is set to underline links */ if (b->underline_links && ISLINK(token)) { redraw_set_colour(colour); bbc_move(xpos, vcent - 7); bbc_draw(xpos + stringwidth, vcent - 7); } /* Put the old graphics window back again. */ restore_graphics_intersection(&r->redraw_area); } } } } /*************************************************/ /* redraw_draw() */ /* */ /* The main browser redraw engine. */ /* */ /* Parameters: A pointer to a browser_data */ /* structure relevant to the redraw; */ /* */ /* A WimpRedrawWindowBlock pointer, */ /* with window area and redraw */ /* rectangle details filled in; */ /* */ /* 1 to plot no backgrounds at all, */ /* else they will be shown; */ /* */ /* 0 for normal redraw, else pointer */ /* to a token where no content is to */ /* be drawn - only the elements that */ /* are needed to indicate selection */ /* should be shown. This is used */ /* mostly for things like removing */ /* borders around images; if bits of */ /* the image have to be redrawn this */ /* can make the removal slow. Only */ /* one token is allowed as any */ /* adjacent images must be redrawn */ /* if the border was plotted over */ /* them, or redraw anomalies will be */ /* seen as 'holes' are left behind. */ /* There is some intelligence to */ /* give different behaviour if */ /* selecting or deselecting things. */ /*************************************************/ _kernel_oserror * redraw_draw(browser_data * b, WimpRedrawWindowBlock * r, int noback, HStream * nocontent) { #ifdef UNIFONT /* Somewhat horrible code for the system font Unicode stuff. */ /* Should be able to lose this eventually. */ _kernel_oserror * e; e = unifont_start_redraw(); if (e) return e; use_noback = noback; e = redraw_draw_r(1, 0, 0, b, b->cell, r, noback, nocontent); if (e) { unifont_end_redraw(); return e; } return unifont_end_redraw(); #else use_noback = noback; return redraw_draw_r(1, 0, 0, b, b->cell, r, noback, nocontent); #endif } /*************************************************/ /* redraw_draw_r() */ /* */ /* Recursive back-end to redraw_draw. */ /* */ /* Parameters: 1 for a top level call, else 0 if */ /* being called recursively; */ /* */ /* X origin for plotting (window */ /* coords); */ /* */ /* Y origin for plotting (window */ /* coords); */ /* */ /* A pointer to a browser_data */ /* structure relevant to the redraw; */ /* */ /* Pointer to a reformat_cell struct */ /* holding the lines and chunks to */ /* redraw; */ /* */ /* A WimpRedrawWindowBlock pointer, */ /* with window area and redraw */ /* rectangle details filled in; */ /* */ /* 1 to plot no backgrounds at all, */ /* else they will be shown; */ /* */ /* 0 for normal redraw, else pointer */ /* to a token where no content is to */ /* be drawn - only the elements that */ /* are needed to indicate selection */ /* should be shown. This is used */ /* mostly for things like removing */ /* borders around images; if bits of */ /* the image have to be redrawn this */ /* can make the removal slow. Only */ /* one token is allowed as any */ /* adjacent images must be redrawn */ /* if the border was plotted over */ /* them, or redraw anomalies will be */ /* seen as 'holes' are left behind. */ /* There is some intelligence to */ /* give different behaviour if */ /* selecting or deselecting things. */ /* */ /* Assumes: Pointers to items may NOT be NULL */ /* unless explicitly stated above. */ /*************************************************/ _kernel_oserror * redraw_draw_r(int toplevel, int xorg, int yorg, browser_data * b, reformat_cell * d, WimpRedrawWindowBlock * r, int noback, HStream * nocontent) { _kernel_oserror * e; browser_data * ancestor = utils_ancestor(b); int more; int l = 0; int page_bottom, page_height; int osxorg, osyorg; BBox wbox, fbox, sbox; #ifdef TRACE if (tl & (1u<<9)) Printf("\nredraw_draw_r: Called\n"); #endif /* Place the x and y origin in OS units into osxorg and osyorg */ convert_pair_to_os(xorg, yorg, &osxorg, &osyorg); /* Start redraw */ do { /* There is an HTML stream, so there is something to redraw. */ sbox = r->redraw_area; /* Set sbox to hold the redraw rectangle details */ /* These corrections are to ensure everything is fully redrawn. */ /* In particular, Font_StringWidth returns widths based on the */ /* distances between adjacent characters. In kerned or italicised */ /* fonts, one character will typically have a leftmost x coord */ /* less than the rightmost point of the previous character. The */ /* reformatter uses StringWidth boxes to maintain correct char */ /* spacing, but then the redraw routines can go wrong, as part of */ /* the (say) rightmost letter may fall outside of the StringWidth */ /* box. Rather than keep two width indications, it's easier just */ /* to add a general correction factor to ensure that all line */ /* chunks are redrawn within a certain 'tolerance' / distance of */ /* the actual redraw rectangle. */ sbox.xmin -= 12, sbox.xmax += 12; sbox.ymin -= 2, sbox.ymax += 2; /* Convert the screen coord redraw rectangle into millipoints */ convert_box_to_points(&sbox, &fbox); /* Convert the screen coord redraw region into work */ /* area coords, putting the result in wbox. */ wbox = sbox; coords_box_toworkarea(&wbox, r); /* Get the page bottom in work area coordinates, and the page height. */ /* This is really a printing only concept, where the visible_area */ /* BBox will in fact hold the entire page bounding box. The variables */ /* aren't used for anything else (at present, hence no 'if' wrapper). */ page_bottom = coords_y_toworkarea(r->visible_area.ymin, r); page_height = r->visible_area.ymax - r->visible_area.ymin; /* Now the main redraw section. */ if (b->nchildren) { /* If this browser has children, it has no directly redrawable content; */ /* however, the frames it contains may need borders drawing around them. */ #ifdef TRACE if (tl & (1u<<9)) Printf("redraw_draw_r: Have children\n"); #endif frames_redraw_borders(b, r); } else { /* If the browser doesn't have child frames, want to draw */ /* the document it holds instead. */ #ifdef TRACE if (tl & (1u<<9)) Printf("redraw_draw_r: Have no children\n"); #endif if (b->stream) { #ifdef TRACE if (tl &512) Printf("redraw_draw_r: Have a document\n"); #endif /* If printing, handle display style override for backgrounds */ if (printing) { /* Force background off for some cases... */ if (printstyle_show_none()) noback = 1; else if (printstyle_show_in_tables_only) { if (toplevel) noback = 1; else { /* Need to work out if this table cell has a background colour */ if (d->cdata && d->cdata[0].t->parent) { if (TD_HAS_BGCOL(d->cdata[0].t->parent)) noback = 0; else noback = 1; } } } /* ...and on for others */ if (printstyle_show_all() && toplevel) noback = 0; } use_noback = noback; /* If we've *not* been told *not* to plot any backgrounds... */ if (!noback) { int htop; if (!controls.swap_bars) htop = toolbars_button_height(b) + toolbars_url_height(b); else htop = toolbars_status_height(b); if (htop) htop += wimpt_dy(); /* If background images are not to be shown, or there's no image */ /* to tile on the background, set the background to a uniform */ /* colour. The 'if' statement implicitly calls the background */ /* image tiler. */ if ( !b->show_background || ( !image_tile_window(b, r, 0, -htop) ) ) { redraw_set_colour(redraw_backcol(b)); bbc_rectanglefill(sbox.xmin, sbox.ymin, sbox.xmax - sbox.xmin + 4, sbox.ymax - sbox.ymin + 4); } } #ifdef TRACE if (tl & (1u<<23)) { BBox rectangle = r->redraw_area; redraw_set_colour(0xff884400); bbc_rectangle(rectangle.xmin,rectangle.ymin,rectangle.xmax-rectangle.xmin-1,rectangle.ymax-rectangle.ymin-1); redraw_set_colour(0xffaa6600); bbc_rectangle(rectangle.xmin+2,rectangle.ymin+2,rectangle.xmax-rectangle.xmin-5,rectangle.ymax-rectangle.ymin-5); redraw_set_colour(0xffcc8800); bbc_rectangle(rectangle.xmin+4,rectangle.ymin+4,rectangle.xmax-rectangle.xmin-9,rectangle.ymax-rectangle.ymin-9); } #endif /* Loop over every line in this cell. */ for (l = 0; l < d->nlines; l++) { /* If there are line chunks for this line, and the bounding box y */ /* coordinates lie within the redraw rectangle, process the line. */ if ( d->ldata[l].n && osyorg + d->ldata[l].y < wbox.ymax && osyorg + d->ldata[l].y + d->ldata[l].h > wbox.ymin ) { HStream * tp; /* Token Pointer */ fm_face h; int x, y; /* Plotting origin */ int keepx, keepy; /* Line's bottom left corner */ int base, i; char * dp; /* Data Pointer */ int cn; /* Chunk Number */ if (printing == 1 && toplevel) { /* If printing = 1, it signals that we're not to split lines */ /* over the bottom of the page. So if this line will drop */ /* off the bottom, we need to be careful. This gets a bit */ /* hacky now... */ /* */ /* To signal to the printing loop that a line was about to */ /* be split but wasn't drawn, the top coord of the line is */ /* returned in the xscroll field of the redraw block passed */ /* to the function. The printing routines use this to work */ /* out where to start the next redraw from. */ /* */ /* However, for large lines - e.g. very big images, tables, */ /* or lines taller than the whole page - we either should, */ /* or in the latter case must, split that line. So, if the */ /* line is taller than 1/PrintSplitFraction of the page */ /* height, will indeed be split over the page boundary. */ /* */ /* If you add code here, remember that xscroll must be */ /* filled in eventually or the printing loop will exit, */ /* assuming there's no more page to draw. */ if (osyorg + d->ldata[l].y < page_bottom) { if (d->ldata[l].h <= page_height / PrintSplitFraction) /* (See Print.h) */ { r->xscroll = osyorg + d->ldata[l].y + d->ldata[l].h; return NULL; } /* The effective 'else' case here has to be handled at the end */ /* of printing, or you've just scrolled the page a long way to */ /* right... */ } } /* Put the base address of the line's chunks into cp, */ /* and point to its associated token in tp. */ cn = d->ldata[l].chunks; tp = d->cdata[cn].t; /* Get the x and y coordinates of the bottom left of the line in */ /* millipoints into keepx and keepy, and the x and y coordinates */ /* of the window origin in millipoints into x and y. */ keepy = d->ldata[l].y; keepx = redraw_start_x(b, d, tp, l); convert_pair_to_points(keepx, keepy, &keepx, &keepy); x = coords_x_toscreen(0, r); y = coords_y_toscreen(0, r); convert_pair_to_points(x, y, &x, &y); /* Offset the line x and y coordinates by the origin passed into the */ /* function, and thus get the actual screen coordinates, in milli- */ /* points, into x and y. */ keepx += xorg; keepy += yorg; x += keepx; y += keepy; /* Set 'base' to hold the baseline offset in millipoints */ convert_to_points(d->ldata[l].b, &base); /* Loop round for up to all the line chunks whilst staying */ /* within the redraw rectangle horizontally. */ for ( i = 0; i < d->ldata[l].n && x < fbox.xmax; x += d->cdata[cn].w, i++, cn++ ) { if (x + d->cdata[cn].w > fbox.xmin) { /* Get the token address for this line chunk */ tp = d->cdata[cn].t; /* If 'selected' is not NULL, and nocontent is specifying th at a */ /* token shouldn't have its contents drawn, then a borders-only */ /* redraw is in progress. For removing a border, when 'selected' */ /* *is* NULL, want to not draw the contents of the given token */ /* but must redraw all others fully, else edge effects will occur */ /* where tokens directly abut the given one (bits can get knocked */ /* out as the border goes but the tokens it was plotted over are */ /* not redraw). */ /* */ /* However, for a borders-only redraw when something is being */ /* selected, don't want to draw the contents of *anything* as the */ /* border wants to overplot it. To effect this, set the nocontent */ /* token to always be the same as the current one. */ if (ancestor->selected && nocontent) nocontent = tp; /* Deal with table tags */ if (tp->tagno == TAG_TABLE) { int oh; convert_to_points(d->ldata[l].h, &oh); /* Use of recursion for redraw... So need to keep this code block */ /* as a code block, don't try to collapse it down a level and */ /* merge in 'oh' above, etc. */ { /* In this case there are table streams hung from d->cdata */ table_stream * table = (table_stream *) tp; table_row * row = NULL; table_headdata * head = NULL; reformat_cell * cellarray = table->cells; reformat_cell * cell; int oldback = 0; int oldaa = 0; int oldbgimage = -1; int t_noback; BBox rbox; BBox * ibox; BBox tbox; int cellindex; int cellcount; int cellmax = table->ColSpan * table->RowSpan; int swap; tbox.xmin = tbox.ymin = 0x1000000; tbox.xmax = tbox.ymax = 0; /* Find out the overall bounding box of the table, and put this in */ /* 'tbox'. */ cellcount = 0; if (cellarray) { cellcount = 0; row = table->List; while (row && cellcount < cellmax) { head = row->List; while (head && cellcount < cellmax) { switch (head->Tag) { case TagTableData: case TagTableHead: { cellindex = head->RowOffs * table->ColSpan + head->ColOffs; if (cellindex < cellmax) { BBox cbox; cell = &cellarray[cellindex]; /* Set the graphics rectangle up for the redraw */ cbox.xmin = x + cell->x; cbox.ymin = y + cell->y + oh - cell->cellheight; cbox.xmax = cbox.xmin + cell->cellwidth; cbox.ymax = cbox.ymin + cell->cellheight; convert_box_to_os(&cbox, &cbox); /* Update the table bounding box as required */ if (cbox.xmin < tbox.xmin) tbox.xmin = cbox.xmin; if (cbox.ymin < tbox.ymin) tbox.ymin = cbox.ymin; if (cbox.xmax > tbox.xmax) tbox.xmax = cbox.xmax; if (cbox.ymax > tbox.ymax) tbox.ymax = cbox.ymax; /* Closure of 'if (cellindex < cellmax)' */ } /* Closure of specific 'case' item */ } break; /* Closure of 'switch (head->Tag)' */ } cellcount ++; head = head->Next; /* Closure of 'while (head && ...)' */ } row = row->Next; /* Closure of 'while (row && ...)' */ } /* tbox doesn't take account of cell spacing yet - correct */ /* for this now. */ tbox.xmin -= table->cellspacing * 2; tbox.ymin -= table->cellspacing * 2; tbox.xmax += table->cellspacing * 2; tbox.ymax += table->cellspacing * 2; /* If the table has a background colour, draw this */ if (TABLE_HAS_BGCOL(table)) { int tx, ty; int tw, th; /* Work out the x and y coordinates of the lower left hand pixel */ /* of the table border, and the width and height of the table */ /* including the border. */ tx = tbox.xmin; ty = tbox.ymin; tw = tbox.xmax - tbox.xmin; th = tbox.ymax - tbox.ymin; /* Draw the background */ redraw_set_colour(TABLE_BGCOL(table)); bbc_rectanglefill(tx, ty, tw - 1, th - 1); } /* Now redraw the table cells */ cellcount = 0; row = table->List; while (row && cellcount < cellmax) { head = row->List; while (head && cellcount < cellmax) { switch (head->Tag) { case TagTableData: case TagTableHead: { cellindex = head->RowOffs * table->ColSpan + head->ColOffs; if (cellindex < cellmax) { cell = &cellarray[cellindex]; #ifdef TRACE if (tl & (1u<<20)) Printf("redraw_draw call: %d -%d\n", keepx + cell->x, -(keepy + cell->y)); #endif if (TD_HAS_BGCOL(head)) { oldback = b->background_colour; oldaa = b->antialias_colour; oldbgimage = b->background_image; b->background_colour = TD_BGCOL(head); b->antialias_colour = b->background_colour; b->background_image = -1; /* For now, no background images in table cells. */ t_noback = 0; } else t_noback = 1; /* Set the graphics rectangle up for the redraw */ rbox.xmin = x + cell->x; rbox.ymin = y + cell->y + oh - cell->cellheight; rbox.xmax = rbox.xmin + cell->cellwidth; rbox.ymax = rbox.ymin + cell->cellheight; convert_box_to_os(&rbox, &rbox); /* (rbox's max coords should be inclusive, not exclusive) */ rbox.xmax--; rbox.ymax--; /* If necessary, swap min and max coords */ if (rbox.xmax < rbox.xmin) swap = rbox.xmax, rbox.xmax = rbox.xmin, rbox.xmin = swap; if (rbox.ymax < rbox.ymin) swap = rbox.ymax, rbox.ymax = rbox.ymin, rbox.ymin = swap; /* See if the cell redraw box intersects the overall redraw rectangle */ if (rbox.xmin == rbox.xmax || rbox.ymin == rbox.ymax) ibox = NULL; else ibox = set_graphics_intersection(&rbox, &r->redraw_area); if (ibox) { BBox oldrect; /* Other routines that set the graphics window do it the same way as */ /* here - they assume the redraw rectangle = the graphics window, as */ /* during printing it's not possible to read the VDU variables to */ /* obtain the actual window. To ensure that this holds true, before */ /* recursively calling the redraw functions, the redraw rectangle */ /* must be set to match the graphics rectangle (and then restored */ /* afterwards). */ oldrect = r->redraw_area; r->redraw_area = *ibox; /* All coords in ibox are inclusive; the max coords in a redraw rectangle */ /* need to be exclusive. */ r->redraw_area.xmax ++; r->redraw_area.ymax ++; /* Recursive call to redraw the cell contents */ redraw_draw_r(0, keepx + cell->x, keepy + cell->y + oh, b, cell, r, t_noback, nocontent); /* Restore the WimpRedrawWindowBlock redraw rectangle */ r->redraw_area = oldrect; /* Restore the actual graphics rectangle */ restore_graphics_intersection(&oldrect); } #ifdef TRACE if (tl & (1u<<11)) { /* Outline the cell BBox */ redraw_set_colour(0xff00aa00); bbc_rectangle(rbox.xmin, rbox.ymin, rbox.xmax - rbox.xmin - 1, rbox.ymax - rbox.ymin - 1); redraw_set_colour(0xff22cc00); bbc_rectangle(rbox.xmin + 2, rbox.ymin + 2, rbox.xmax - rbox.xmin - 5, rbox.ymax - rbox.ymin - 5); } #endif /* Restore any data altered in b */ if (!t_noback) { b->background_colour = oldback; b->antialias_colour = oldaa; b->background_image = oldbgimage; } /* Draw the slabbed in cell border. */ if (TABLE_BORDER(table)) { int dx = wimpt_dx(); int dy = wimpt_dy(); int hx = dx - 1; int hy = dy - 1; int max; int cx, cy; int cw, ch; /* Get the cell x,y and w,h in OS units from the redraw box. We want to */ /* use this information rather than cell->x etc. as the redraw box is */ /* the item that any internal redraws will have adhered to, including */ /* plotting cell backgrounds (if present). Thus, we want any table */ /* borders to be based on those same coordinates. */ cx = rbox.xmin; cy = rbox.ymin; cw = rbox.xmax - rbox.xmin + 1; ch = rbox.ymax - rbox.ymin + 1; /* Get the maximum horizontal OS to pixel scaling values, as this is */ /* used as a threshold for the 2D border / 3D border switching. */ if (dy > dx) max = dy; else max = dx; #ifdef TRACE if (tl & (1u<<20)) Printf("cell box at %d %d %d %d\n",cx,cy,cw,ch); #endif /* Don't do any actual drawing if the Choices don't say so. We have to */ /* do the calculation stuff above (well, most of it...!) so that the */ /* outer border plotter code (below) will work, whether inner borders */ /* are plotted or not. */ if (choices.table_inner != Choices_TableInner_Never) { /* For tables with a cell spacing greater than the OS unit */ /* to pixel scaling value, use a 3D border (depending on */ /* what is specified in the Choices). */ if ( choices.table_inner != Choices_TableInner_Always2D && ( table->cellspacing > max || choices.table_inner == Choices_TableInner_Always3D ) ) { redraw_set_colour(Redraw_Colour_AlmostWhite); bbc_rectanglefill(cx, cy - dy, cw - hx, hy); bbc_rectanglefill(cx + cw, cy - dy, hx, ch + hy); redraw_set_colour(Redraw_Colour_PlinthGrey); bbc_rectanglefill(cx - dx, cy - dy, hx, ch + dy + hy); bbc_rectanglefill(cx, cy + ch, cw + hx, hy); } /* Otherwise use a black 2D border. If we've got half of the OS unit to */ /* pixel spacing available, then there'll be at least 1 pixel between */ /* all cells so we can draw in that gap. Otherwise, we must plot over */ /* the cell contents. */ else if (table->cellspacing >= (max >> 1)) { redraw_set_colour(Redraw_Colour_Black); bbc_rectanglefill(cx - dx, cy - dy, cw + hx, hy); bbc_rectanglefill(cx + cw, cy - dy, hx, ch + hy); bbc_rectanglefill(cx - dx, cy, hx, ch + hy); bbc_rectanglefill(cx, cy + ch, cw + hx, hy); } else { redraw_set_colour(Redraw_Colour_Black); bbc_rectanglefill(cx, cy, cw - hx, hy); bbc_rectanglefill(cx + cw - dx, cy, hx, ch - hy); bbc_rectanglefill(cx, cy, hx, ch - hy); bbc_rectanglefill(cx, cy + ch - dy, cw - hx, hy); } } } /* Closure of 'if (cellindex < cellmax)' */ } /* Closure of specific 'case' item */ } break; /* Closure of 'switch (head->Tag)' */ } cellcount ++; head = head->Next; /* Closure of 'while (head && ...)' */ } row = row->Next; /* Closure of 'while (row && ...)' */ } /* Right, now redraw the slabbed out outer table border */ if (TABLE_BORDER(table) && choices.table_outer != Choices_TableOuter_Never) { int dx = wimpt_dx(); int dy = wimpt_dy(); int max; int tx, ty; int tw, th; int tb; tb = TABLE_BORDER(table) * 2; /* 1 'web pixel' = 2 OS */ /* Work out the x and y coordinates of the lower left hand pixel */ /* of the table border, and the width and height of the table */ /* including the border. */ tx = tbox.xmin - tb; ty = tbox.ymin - tb; tw = tbox.xmax - tbox.xmin + tb * 2; th = tbox.ymax - tbox.ymin + tb * 2; /* Get the maximum horizontal OS to pixel scaling values, as this is */ /* used as a threshold for the 2D border / 3D border switching. */ if (dy > dx) max = dy; else max = dx; /* Use the same threshold value on the border as for internal cell borders */ if ( choices.table_outer != Choices_TableOuter_Always2D && ( table->cellspacing > max || choices.table_outer == Choices_TableOuter_Always3D ) ) { int x[4], y[4]; /* There are 8 corners to a plinth, 6------4 */ /* we draw using 8 triangles. Work |\ /| */ /* these out first in the arrays | 7--5 | */ /* declared above to make the | | | | */ /* plotting code tidier and avoid | 1--3 | */ /* unnecessary recalculation of |/ \| */ /* corner coordinates. 0------2 */ /* */ /* Note how there are only 4 unique x or y */ /* values, so that's all we need to work out. */ x[0] = tx; x[1] = tx + tb - 1; x[2] = tx + tw - tb; x[3] = tx + tw - 1; y[0] = ty; y[1] = ty + tb - 1; y[2] = ty + th - tb; y[3] = ty + th - 1; /* OK, now do the drawing. We need to be careful about */ /* the direction that the drawing occurs to ensure that */ /* adjacent diagonal lines meet up correctly. */ redraw_set_colour(Redraw_Colour_PlinthGrey); /* Bottom edge */ bbc_trianglefill(x[0], y[0], x[1], y[1], x[3], y[0]); /* 0 -> 1 -> 2 */ bbc_trianglefill(x[1], y[1], x[3], y[0], x[2], y[1]); /* 1 -> 2 -> 3 */ /* Right hand edge */ bbc_trianglefill(x[3], y[0], x[2], y[1], x[3], y[3]); /* 2 -> 3 -> 4 */ bbc_trianglefill(x[2], y[1], x[3], y[3], x[2], y[2]); /* 3 -> 4 -> 5 */ /* Now the lighter section */ redraw_set_colour(Redraw_Colour_AlmostWhite); /* Top edge */ bbc_trianglefill(x[3], y[3], x[2], y[2], x[0], y[3]); /* 4 -> 5 -> 6 */ bbc_trianglefill(x[2], y[2], x[0], y[3], x[1], y[2]); /* 5 -> 6 -> 7 */ /* Finally, the left hand edge. */ bbc_trianglefill(x[0], y[3], x[1], y[2], x[0], y[0]); /* 6 -> 7 -> 0 */ bbc_trianglefill(x[1], y[2], x[0], y[0], x[1], y[1]); /* 7 -> 0 -> 1 */ } /* Otherwise, use a 2D outer border. */ else { redraw_set_colour(Redraw_Colour_Black); bbc_rectanglefill(tx, ty, tw - 1, tb - 1); bbc_rectanglefill(tx, ty + th - tb, tw - 1, tb - 1); bbc_rectanglefill(tx, ty, tb - 1, th - 1); bbc_rectanglefill(tx + tw - tb, ty, tb - 1, th - 1); } } /* Closure of 'if (cellarray)' */ } /* Closure of unconditional code block dealing with redrawing */ /* the body of a table. */ } } /* Deal with forms elements */ else if (tp->tagno == TAG_INPUT || tp->tagno == TAG_TEXTAREA || tp->tagno == TAG_SELECT) { /* A text-based element */ if ( tp->tagno == TAG_TEXTAREA || tp->tagno == TAG_SELECT || HtmlINPUTtype(tp) == inputtype_TEXT || HtmlINPUTtype(tp) == inputtype_PASSWORD ) { BBox box; int ox, oy; fm_face fh; convert_pair_to_os(x, y + base, &ox, &oy); fh = fm_find_token_font(b, tp, 0); fm_font_box(fh, &box); /* Set up the bounding box for a text area, with a minimum of 2 rows */ if (tp->tagno == TAG_TEXTAREA) { int r; int lh, lb; form_get_linesize(&box, &lh, &lb); r = tp->rows; if (r < 2) r = 2; box.ymin -= lh * (r - 1); /* ymin is already below the first line, so want to drop it by (rows - 1) more */ } /* Account for the borders */ box.ymin = box.ymin + oy - 8; box.ymax = box.ymax + oy + 8; box.xmin = ox + 4; convert_to_os(x + d->cdata[cn].w, &box.xmax); /* Account for a border */ box.xmax -= 4; if (nocontent != tp) { redraw_input_field(b, tp, &box, redraw_token_colour(b, tp), tp->tagno == TAG_SELECT); fm_set_font_colour(fh, redraw_token_colour(b, tp), (tp->tagno == TAG_SELECT) ? Redraw_Colour_BackGrey : Redraw_Colour_White); form_textarea_redraw(b, d->cdata[cn].t, &box, &r->redraw_area, fh, tp->tagno == TAG_TEXTAREA, tp->tagno == TAG_INPUT && HtmlINPUTtype(tp) == inputtype_PASSWORD); } /* If the element is a SELECT field, it needs a menu icon too */ if (tp->tagno == TAG_SELECT) { int width, height, offset; BBox icon; /* Get the sprite size, work out a bounding box and plot */ /* this as a virtual icon. */ read_sprite_size("fgright", &width, &height); /* Allow for the border */ width += 4; height += 4; /* Work out the vertical offset */ offset = (box.ymax - box.ymin - height) / 2; icon.xmin = box.xmax - width; icon.ymin = box.ymin + offset; icon.xmax = box.xmax; icon.ymax = box.ymin + offset + height; if (redraw_selected(b, tp)) redraw_border_around_box(&icon, b->selected_colour); coords_box_toworkarea(&icon, r); if (nocontent != tp) { WimpPlotIconBlock block; block.bbox = icon; block.flags = 0x1700311A; if (redraw_backcol(b) != Redraw_Colour_BackGrey) block.flags |= (1<<2); /* Border if not using Wimp grey background */ block.data.is.sprite = "fgright"; block.data.is.sprite_area = (void *) sprite_block; block.data.is.sprite_name_length = sizeof("fgright") - 1; wimp_plot_icon(&block); } } } else switch(HtmlINPUTtype(tp)) { /* Graphics-based forms elements */ case inputtype_CHECKBOX: { if (nocontent != tp) redraw_switch(b, tp, x, y + base, form_get_field(b, d->cdata[cn].t) -> checked ? "fopton" : "foptoff", r); } break; case inputtype_RADIO: { if (nocontent != tp) redraw_switch(b, tp, x, y + base, form_get_field(b, d->cdata[cn].t) -> checked ? "fradioon" : "fradiooff", r); } break; case inputtype_IMAGE: goto do_image; /* See a short distance below */ case inputtype_HIDDEN: break; case inputtype_SUBMIT: /* SUBMIT same as RESET: no break */ case inputtype_BUTTON: /* Again, no break */ case inputtype_RESET: { BBox box; int fh, ox, oy, colour; const char * p; p = form_button_text(tp); convert_pair_to_os(x, y + base, &ox, &oy); fh = fm_find_token_font(b, tp, 0); fm_font_box(fh,&box); box.ymin = box.ymin + oy - 8; box.ymax = box.ymax + oy + 8; box.xmin = ox + 4; convert_to_os(x + d->cdata[cn].w,&box.xmax); /* Account for a border */ box.xmax -= 4; /* Draw the button's plinth */ colour = redraw_token_colour(b, tp); if (b->highlight == tp) { if (nocontent != tp) { redraw_button(b, tp, &box, 1); fm_set_font_colour(fh, colour, Redraw_Colour_MidGrey); } } else { if (nocontent != tp) { redraw_button(b, tp, &box, 0); fm_set_font_colour(fh, colour, Redraw_Colour_BackGrey); } } /* Plot the text, centred horizontally */ if (p && nocontent != tp) { int length, end, width; length = strlen(p); end = 0; while(end < length && p[end] != '\n') end++; e = fm_get_string_width(fh, p, 0x1000000, end - d->cdata[cn].o, -1, NULL, &width); width = (d->cdata[cn].w - width) / 2 + 4; if (width < 0) width = 0; fm_puts(fh, x + width, y + base, p, 0, 0); } } break; } } /* Plot an image */ else if (tp->style & IMG) { BBox box; int ox, oy, o; do_image: /* (This code is also used for form INPUT TYPE=IMAGE tags; see above) */ convert_pair_to_os(x, y + base, &ox, &oy); if (!reformat_get_image_size(b, tp, &box)) { /* Correct the coordinates for plotting */ ox -= box.xmin; box.xmin += ox; box.ymin += oy; box.xmax += ox; box.ymax += oy; /* Find the border width (if any) */ o = (tp->style & IMG) ? tp->maxlen * 2 : 0; /* Ensure the image has the plotting position recorded within */ /* it's associated image_info structure, so that update */ /* routines elsewhere will know where to plot it */ image_set_token_image_position(b, tp, coords_x_toworkarea(box.xmin + o, r), coords_y_toworkarea(box.ymin + o, r)); /* Draw a border of tp->maxlen * 2 OS units width around an image. */ /* This comes from the image's BORDER attribute. Links default to */ /* a 2 pixel (4 OS unit) border in HTMLLib, whereas non-link */ /* images default to no colour. */ if (tp->tagno == TAG_INPUT && redraw_selected(b, tp)) { redraw_border_around_box(&box, b->selected_colour); } else { if (o) { redraw_set_colour(redraw_token_colour(b, tp)); bbc_rectanglefill(box.xmin, box.ymin, box.xmax - box.xmin - 1, o - 1); bbc_rectanglefill(box.xmin, box.ymin, o - 1, box.ymax - box.ymin - 1); bbc_rectanglefill(box.xmin, box.ymax - o, box.xmax - box.xmin - 1, o - 1); bbc_rectanglefill(box.xmax - o, box.ymin, o - 1, box.ymax - box.ymin - 1); } else if (redraw_selected(b, tp)) { redraw_border_around_box(&box, b->selected_colour); // Hmm. Need to find a // way of invoking this // reliably, and clearing // the highlight without // flicking horribly. // // /* Things get rather more complicated for client side maps, if */ // /* the pointer is over them. */ // // if (b->pointer_over == tp && (tp->type & TYPE_ISCLIENTMAP)) // { // /* Ask the client side map handler to do this bit */ // // csim_highlight_region(b, b->selected_colour, box.xmin + o, box.ymax - o); // } } } /* Redraw the image itself */ if (nocontent != tp) RetError(image_redraw(b, r, d->cdata[cn].t, o + box.xmin, o + box.ymin)); } } /* Plot an OBJECT, EMBED or APPLET tag */ else if (ISOBJECT(tp)) { BBox box; int ox, oy, o; convert_pair_to_os(x, y + base, &ox, &oy); if (!reformat_get_object_size(b, tp, &box)) { o = HtmlOBJECTborder(tp) * 2; ox -= box.xmin; box.xmin += ox; box.ymin += oy; box.xmax += ox; box.ymax += oy; object_set_token_object_position(b, tp, coords_x_toworkarea(box.xmin + o, r), coords_y_toworkarea(box.ymin + o, r)); /* Draw a border, if required */ if (HtmlOBJECTborder(tp)) { if (o) { redraw_set_colour(redraw_token_colour(b, tp)); bbc_rectanglefill(box.xmin, box.ymin, box.xmax - box.xmin - 1, o - 1); bbc_rectanglefill(box.xmin, box.ymin, o - 1, box.ymax - box.ymin - 1); bbc_rectanglefill(box.xmin, box.ymax - o, box.xmax - box.xmin - 1, o - 1); bbc_rectanglefill(box.xmax - o, box.ymin, o - 1, box.ymax - box.ymin - 1); } else if (redraw_selected(b, tp)) { redraw_border_around_box(&box, b->selected_colour); } } if (nocontent != tp) RetError(object_redraw(b, r, d->cdata[cn].t, o + box.xmin, o + box.ymin)); } } /* Plot a horizontal rule */ else if (tp->style & HR) { int w, h, lmarg, ox, oy = 0; convert_to_os(y, &oy); lmarg = redraw_start_x(b, d, tp, l); convert_to_os(d->cdata[cn].w, &w); /* Round width to a multiple of 2 and limit check it. */ /* Allow sizes greater than the available width, in */ /* which case align to the left and draw to whatever */ /* width was requested. */ w = w &~ 1; if (w < 2) w = 2; /* Deal with a size (height) specifier */ if (HR_HAS_SIZE(tp)) { /* Currently only recognise pixels */ switch (HR_SIZE_UNITS(tp)) { case UNITS_PIXELS: h = HR_SIZE(tp) * 2; break; /* (IMPORTANT: If adding extra units, ensure h ends up a multiple of 2) */ default: h = 4; break; } } else h = 4; /* Limit check the height */ if (h < 2) h = 2; /* Sort out the horizontal and vertical plotting offsets; */ /* centre vertically, and align horizontally as specified */ /* in the token. */ oy += ((d->ldata[l].h - h) / 2) &~3 - 4; oy += 6; ox = (coords_x_toscreen(lmarg + osxorg, r) &~1); /* Plot a black rule if NOSHADE is specified or the height */ /* or width are less than 4 OS units, else plot a '3D' */ /* rule. */ if (HR_NOSHADE(tp) || h < 4 || w < 4) { redraw_set_colour(0); bbc_rectanglefill(ox, oy, w - 1, h - 1); } else { if (h == 4) { /* Simple 'groove' rule */ h = h / 2; redraw_set_colour(Redraw_Colour_MidGrey); bbc_rectanglefill(ox, oy, w - 1, h - 1); redraw_set_colour(Redraw_Colour_AlmostWhite); bbc_rectanglefill(ox, oy - h, w - 1, h - 1); } else { /* 3D 'box' rule */ redraw_set_colour(Redraw_Colour_AlmostWhite); bbc_rectanglefill(ox, oy, w - 1, 1); bbc_rectanglefill(ox + w - 2, oy, 1, h - 1); redraw_set_colour(Redraw_Colour_MidGrey); bbc_rectanglefill(ox, oy + h - 2, w - 1, 1); bbc_rectanglefill(ox, oy, 1, h - 1); } } } /* Plot a bullet point */ else if(ISBULLET(tp)) { int ox,oy; convert_pair_to_os(x, y + base, &ox, &oy); redraw_bullet(ox, oy, tp->indent, r); } /* Plot some text */ else { dp = d->cdata[cn].t->text; if (dp) { BBox size; int c, yofs, height; /* Find the font handle for the token, and its colour */ h = fm_find_token_font(b, tp, 0); c = redraw_token_colour(b, tp); /* Find the text height in OS units */ fm_font_box(h, &size); height = size.ymax - size.ymin; /* Work out the y offset to plot at */ if (ISSUP(tp)) { /* Shift baseline up for superscript text. The following */ /* will be for the SUP size text, remember... */ convert_to_points(height, &yofs); /* SUP height = normht * 3 / 5, so to get normal */ /* height from SUP do height * 5 / 3. Then want */ /* to get the height remaining and use this as an */ /* addition for the y positioning, so need to add */ /* (normht - hormht * 3 / 5) = normht * 2 / 5. */ /* This all simplifies out to height * 2 / 3, but */ /* this looks too high in practice, so it's taken */ /* down a bit from that! */ yofs = y + base + (yofs / 2); } else if (ISSUB(tp)) { /* Shift the baseline down a bit for subscript text */ yofs = y + (base * 4) / 5; } else yofs = y + base; /* Set the font colour and plot the text */ fm_set_font_colour(h,c,redraw_background_colour(b,c)); if (dp) fm_putsl(h, x, yofs, dp + d->cdata[cn].o, d->cdata[cn].l, 0, b->background_image >= 0 && b->show_background); /* Deal with underlining. The position should not be affected */ /* by SUB or SUP text. */ if ( ( ( ISLINK(tp) && b->underline_links ) || ISUNDERLINE(tp) ) && !(tp->tagno == TAG_TABLE) ) { /* Underline the item - set the colour, and start at the item's x coordinate... */ int ox,oy; /* Set the colour */ redraw_set_colour(redraw_token_colour(b, tp)); /* Work out the coordinates (in OS units) */ convert_pair_to_os(x, y + base, &ox, &oy); oy -= 7; /* Move to the start point */ bbc_move(ox, oy); /* ...finish at x plus its width. */ convert_to_os(x + d->cdata[cn].w, &ox); bbc_draw(ox, oy); } /* Deal with STRIKE text. This needs to have the strikethrough */ /* line through the text middle, as opposed to following the */ /* body text font baseline (so SUB and SUP *will* have an */ /* effect on the positioning). */ if (ISSTRIKE(tp) && !(tp->tagno == TAG_TABLE)) { int ox, oy; int hs; redraw_set_colour(redraw_token_colour(b, tp)); convert_pair_to_os(x, yofs, &ox, &oy); hs = height / 4; oy += hs; bbc_move(ox, oy); convert_to_os(x + d->cdata[cn].w, &ox); bbc_draw(ox, oy); } } } // Plot the bounding box of any object; green to // mark an image, else red #ifdef TRACE if ((tl & (1u<<11)) || (tl & (1u<<19))) { int ox, oy, ow, oh; convert_pair_to_os(x, y, &ox, &oy); convert_to_os(d->cdata[cn].w, &ow); oh = d->ldata[l].h; if (tl & (1u<<11)) { _swix(Wimp_SetColour, _IN(0), (tp->style & IMG) ? 10 : 11); bbc_rectangle(ox, oy, ow - 1, oh - 1); } // Mark tokens with no lower bits set in the type word // (so not head, body, frameset etc.) and a NULL text // field, with a magenta dot in the bottom *right* of // the token BBox and a cyan dot in the top right of the // BBox respectively. if (tl & (1u<<19)) { if (!(tp->type & 0xff)) { redraw_set_colour(0xff00ff00); bbc_circlefill(ox + ow - 1, oy, 6); redraw_set_colour(0); bbc_circle(ox + ow - 1, oy, 6); } if (!tp->text) { redraw_set_colour(0xffff0000); bbc_circlefill(ox + ow - 1, oy + oh - 1, 6); redraw_set_colour(0); bbc_circle(ox + ow - 1, oy + oh - 1, 6); } } } #endif /* Closure of long 'if' checking if the current chunk */ /* lies partially or entirely within the redraw area. */ /* If it does, the code above executes. */ } /* Closure of 'for' looping round chunks on a given line */ /* that lies partially or entirely within the redraw */ /* area. */ } /* For printing, tell the print routines where we were up to */ if ( printing == 1 && toplevel && osyorg + d->ldata[l].y < page_bottom ) r->xscroll = osyorg + page_bottom; /* Closure of long 'if' checking if the current line lies */ /* partially or entirely within the redraw area. The code */ /* above executes if it does. */ } /* Closure of 'for' looping for all lines in the document. */ } /* Closure of long 'if' checking if d->stream was not NULL. */ /* If not, then there is a document to plot; so execute the */ /* above code. Else, execute the code below. */ } else { if (!printing || !toplevel) { /* Set the graphics background colour to the default */ /* and clear the graphics rectangle [to this colour]. */ redraw_set_colour(choices.background_colour); bbc_rectanglefill(r->redraw_area.xmin, r->redraw_area.ymin, r->redraw_area.xmax - r->redraw_area.xmin, r->redraw_area.ymax - r->redraw_area.ymin); } /* If there's a fetch URL but no stream, the document was empty */ if (browser_current_url(b) && !fetch_fetching(b)) { fm_face h; int x, y, htop; BBox size; /* Claim a font */ h = fm_find_font(b, "sans", (int) (choices.font_size * 1.5), (int) (choices.font_size * 1.5), 0, 1); /* Find the height of the tallest character */ fm_font_box(h, &size); /* Use that height, and the toolbar sizes to work out the y coordinate to plot at */ if (!controls.swap_bars) htop = toolbars_button_height(b) + toolbars_url_height(b); else htop = toolbars_status_height(b); if (htop) htop += wimpt_dy(); y = coords_y_toscreen(htop - size.ymax - size.ymin - 40, r); /* -40 = arbitrary constant, aesthetic consideration */ /* The x coordinate has a fixed offset from the left */ x = coords_x_toscreen(32,r); #ifdef TRACE if (tl & (1u<<9)) { Printf("redraw_draw_r: Empty page, claimed font %p\n",(void *) h); Printf(" Plotting x,y %d,%d\n",x,y); } #endif /* Set a black-on-grey font colour */ fm_set_font_colour(h, choices.text_colour, choices.background_colour); /* Write the string */ fm_puts(h, x, y, lookup_token("NoData:The server returned a blank page.", 0, 0), 1, 1); if (printing) { r->xscroll = 0; return NULL; } } } /* Closure of long 'if' checking if the browser window had */ /* children. If not, the code immediately above - normal */ /* redraw - may be run, else special frame border redraw */ /* code is run. */ } #ifdef ANTI_TWITTER if (!printing && toplevel) anti_twitter(r); #endif if (!printing && toplevel) wimp_get_rectangle(r,&more); else more = 0; } while (more); /* Finished... */ return NULL; }