/* 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 "Fetch.h" #include "FontManage.h" #include "Forms.h" #include "Frames.h" #include "Images.h" #include "Reformat.h" #include "Tables.h" #include "TokenUtils.h" #include "Toolbars.h" #include "Redraw.h" /* Static function prototypes */ static void redraw_border_around_box (BBox * box, int colour); 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); /* 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.col_back = %p\n",(void *) choices.col_back); #endif return (((b->backgroundcol == -1) || (!b->sourcecolours)) ? (choices.col_back) : (b->backgroundcol)); } /*************************************************/ /* 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->plainback) return redraw_backcol(b); switch (b->aacol) { /* Defeat anti-aliasing by giving the same background colour */ /* as the foreground if aacol is -1. */ case -1: return foregroundcolour; /* If aacol 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->backgroundcol == -1 ? foregroundcolour : b->backgroundcol); } /* Return either the default background colour or the anti-alias */ /* colour, depending on whether document colour overriding is */ /* on or off respectively. */ return (!b->sourcecolours ? choices.col_back : b->aacol); } /*************************************************/ /* 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->style & (INPUT | TEXTAREA | SELECT)) return 0; /* 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->sourcecolours ? b->follcol : choices.col_foll); /* If tokens are selected, return the appropriate colour */ if (redraw_selected(b, t)) return (b->sourcecolours ? b->selecol : choices.col_sele); /* 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->sourcecolours ? b->usedcol : choices.col_used); return (b->sourcecolours ? b->linkcol : choices.col_link); } /* If the token has attached specific colour information, return that */ if ((t->type & TYPE_COLOURED) && b->sourcecolours) return (t->colour << 8); /* If the token is just text, return the normal text colour */ return (b->sourcecolours ? b->textcol : choices.col_text); } /*************************************************/ /* 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->table) return b->display_width; else { int osw; convert_to_os(d->cellwidth, &osw); return osw; } } /*************************************************/ /* 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->table) return b->leftmargin; else return 0; /* Cell margins (cellpaddings) are handled in the tables routines */ } /*************************************************/ /* 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->table) return b->rightmargin; else return 0; /* Cell margins (cellpaddings) are handled in the tables routines */ } /*************************************************/ /* redraw_margin() */ /* */ /* Works out the left hand indented margin 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; */ /* */ /* Pointer to a token holding */ /* indentation information. */ /* */ /* Returns: The left hand margin, taking */ /* account of list indentations */ /* etc., in millipoints. */ /*************************************************/ int redraw_margin(browser_data * b, reformat_cell * d, HStream * t) { int s, i; s = t->style; i = t->indent * b->leftindent; if (t->tag == TABLE && ISBODY(t)) return redraw_left_margin(b, d); /* Indent more depending on the header type, if the */ /* HStream represents a header. */ if (redraw_header(s)) return i + redraw_left_margin(b, d); // if (redraw_header(s) == 1 || redraw_header(s) == 2) return (i + 4); // if (redraw_header(s) == 3) return (i + 16 + 4); // if (redraw_header(s) == 4 || redraw_header(s) == 5) return (i + 32 + 4); /* Add an appropriate amount for a bullet point; the */ /* ISBULLET macro is defined in Fetch.h */ if (t->indent && !ISBULLET(t)) { int width; convert_to_points(reformat_bullet_width(t->indent), &width); i += width; } /* Add an amount for block quote or address text; the */ /* constants are defined in HTMLLib:tags.h */ if (s & (BLOCKQUOTE | ADDRESS)) return i + redraw_left_margin(b, d) + b->quotemargin; /* Return a general indent based on the total summed so */ /* far plus an extra amount for lists etc. */ return i + redraw_left_margin(b, d); } /*************************************************/ /* 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 positioning 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 = cell->ldata[line].chunks; int align = 0; /* If the chunk is a horizontal rule, want to be able to have this */ /* starting at the far left hand edge. */ if (t->style & HR) return 0; /* If the token isn't centred just return the margin value; */ /* else work out the centre alignment indentation. */ if ((t->style & CENTER) || ((t->type & TYPE_ALIGN_MASK) == TYPE_CENTRE)) align = 1; if ((t->type & TYPE_ALIGN_MASK) == TYPE_RIGHT) align = 2; if (align) { int i; /* 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++); /* For centred objects, divide by 2 to get the indent in millipoints */ if (align == 1) x /= 2; /* Sanity check */ if (x < 0) x = redraw_left_margin(b, cell); /* Convert back to OS units */ convert_to_os(x, &x); return x; } convert_to_os(redraw_margin(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; /* 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. */ /* */ /* 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; /* 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 = b->ancestor; int found = 0; if (!ancestor) ancestor = b; 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()). */ /*************************************************/ static void redraw_border_around_box(BBox * rbox, int colour) { BBox box; box = *rbox; box.xmin &= ~1; box.ymin &= ~1; box.xmax &= ~1; box.ymax &= ~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->selecol); } } /*************************************************/ /* 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->selecol); } /*************************************************/ /* 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; char validation[32]; 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 = ox + w; icon.ymax = oy + h; if (r) coords_box_toworkarea(&icon, r); sprintf(validation,"S%s",spr); block.bbox = icon; block.flags = 0x1700311B; block.data.ist.buffer = ""; block.data.ist.validation = validation; block.data.ist.buffer_size = 4; wimp_plot_icon(&block); if (redraw_selected(b, t)) { if (r) coords_box_toscreen(&icon, r); redraw_border_around_box(&icon, b->selecol); } } /*************************************************/ /* 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[20]; int w, h; BBox icon; WimpPlotIconBlock block; sprintf(spr, "Sb%d",(bullet + bullets - 1) % bullets); if (read_sprite_size(spr + 1, &w, &h)) w = h = 32; icon.xmin = x; icon.ymin = y; icon.xmax = x + w; icon.ymax = y + h; if (r) coords_box_toworkarea(&icon, r); block.bbox = icon; block.flags = 0x1700311B; block.data.ist.buffer = ""; block.data.ist.validation = spr; block.data.ist.buffer_size = 4; wimp_plot_icon(&block); } /*************************************************/ /* 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) { return redraw_draw_r(1, 0, 0, b, b->cell, r, noback, nocontent); } /*************************************************/ /* 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. */ /*************************************************/ _kernel_oserror * redraw_draw_r(int toplevel, int xorg, int yorg, browser_data * b, reformat_cell * d, WimpRedrawWindowBlock * r, int noback, HStream * nocontent) { int l = 0; int more, page_bottom = 0; BBox wbox,fbox,sbox; int osxorg, osyorg; _kernel_oserror * e; browser_data * ancestor = b->ancestor; if (!ancestor) ancestor = b; #ifdef TRACE if (tl & (1u<<9)) Printf("\nredraw_draw: 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. */ wbox = r->redraw_area; /* Set wbox 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. */ wbox.xmin -= 12, wbox.xmax += 12; wbox.ymin -= 2, wbox.ymax += 2; /* Retain the screen coord version of the redraw rectangle in sbox */ /* (wbox holds it now, but this is converted to work area coords */ /* in a moment). Put the screen coordinate version in fbox, with */ /* the OS unit coords converted to millipoints. */ sbox = wbox; convert_box_to_points(&sbox, &fbox); /* Convert the visible area of the window to work area */ /* coords, putting the result in wbox. */ coords_box_toworkarea(&wbox,r); /* Get the page bottom in work area coordinates. This is a printing only */ /* concept, where the visible_area BBox will in fact hold the entire */ /* page bounding box. */ page_bottom = coords_y_toworkarea(r->visible_area.ymin, r); // if (b->nlines) // { // Printf("\n*** LINE ENUMERATION ***\n\nNumber of lines: %d\n\n",b->nlines); // // for (l = 0; l < b->nlines; l++) // { // Printf("Line %d\n-y : %d\nh : %d\nb : %d\nn : %d\nchunks: %d\n\n", // l, // -d->ldata[l].y, // d->ldata[l].h, // d->ldata[l].b, // d->ldata[l].n, // d->ldata[l].chunks); // } // // Printf("\n************************\n\n"); // } // else Printf("\n******* NO LINES *******\n\n"); if (b->nchildren) { int child; unsigned int colour; browser_data * c; WimpGetWindowOutlineBlock co; /* If the window has children, want to draw frame borders, not */ /* any document content. */ #ifdef TRACE if (tl & (1u<<9)) Printf("redraw_draw: Have children\n"); #endif /* The bottom bit of the colour field is set to indicate */ /* that a colour is present, as opposed to having no */ /* colour set. In the latter case default to background */ /* grey, in the former strip off the set bit. Then set */ /* that colour. */ colour = b->frameset->maxlen; if (!(colour & 1)) colour = Redraw_Colour_WNGrey; else colour &= ~1; redraw_set_colour(colour); 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 /* Now loop round the children, drawing a 3D border around them */ /* for border spacings >= 2, a black line for spacing 1, or */ /* nothing for spacing 0 (shouldn't then get redraw events for */ /* the parent, but you never know - e.g. user could have got a */ /* patch that allowed the frames to be moved by dragging on */ /* their work area). */ if (b->frameset->indent) /* Holds frame spacing (equiv. to border width) */ { for (child = 0; child < b->nchildren; child ++) { c = b->children[child]; co.window_handle = c->window_handle; if (!wimp_get_window_outline(&co)) { if (b->frameset->indent >= 2) redraw_set_colour(Redraw_Colour_AlmostWhite); else redraw_set_colour(0); /* Bottom edge */ bbc_rectanglefill(co.outline.xmin, co.outline.ymin - 2, co.outline.xmax - co.outline.xmin + 1, 1); bbc_rectanglefill(co.outline.xmax, co.outline.ymin, 1, co.outline.ymax - co.outline.ymin - 1); /* Right hand edge */ if (b->frameset->indent >= 2) redraw_set_colour(Redraw_Colour_PlinthGrey); /* Top edge */ bbc_rectanglefill(co.outline.xmin - 2, co.outline.ymax, co.outline.xmax - co.outline.xmin + 3, 1); /* Left hand edge */ bbc_rectanglefill(co.outline.xmin - 2, co.outline.ymin - 2, 1, co.outline.ymax - co.outline.ymin + 1); /* If the edges are draggable - for frame resizing - plot icons */ /* indicate this. */ { int width, height; BBox icon; /* Get the sprite size, work out a bounding box and plot */ /* this as a virtual icon. */ read_sprite_size("resizeframe", &width, &height); /* Can only plot sprites if there's room... The additions to width and */ /* height account for the border plotted above. */ if ( width + 4 <= b->frameset->indent * wimpt_dx() && height + 4 <= b->frameset->indent * wimpt_dy() ) { WimpPlotIconBlock block; block.flags = 0x1700311B; block.data.ist.buffer = ""; block.data.ist.validation = "Sresizeframe"; block.data.ist.buffer_size = 4; if (frames_can_resize_right(b, child)) { /* Plot to right of frame */ icon.xmin = co.outline.xmax + b->frameset->indent - width / 2; icon.ymin = co.outline.ymin + (co.outline.ymax - co.outline.ymin - height) / 2; icon.xmax = icon.xmin + width; icon.ymax = icon.ymin + height; coords_box_toworkarea(&icon, r); block.bbox = icon; wimp_plot_icon(&block); } if (frames_can_resize_bottom(b, child)) { /* Plot below the frame */ icon.xmin = co.outline.xmin + (co.outline.xmax - co.outline.xmin - width) / 2; icon.ymin = co.outline.ymin - height / 2 - b->frameset->indent; icon.xmax = icon.xmin + width; icon.ymax = icon.ymin + height; coords_box_toworkarea(&icon, r); block.bbox = icon; wimp_plot_icon(&block); } /* Closure of long 'if' checking that there is room to plot */ /* a resize handle for this frame, given that the frame */ /* itself doesn't have noresize specified. The code above */ /* executes if noresize is not specified and there's room */ /* to do the plot. */ } /* Closure of code block dealing with resize handle plotting */ } /* Closure of long 'if' ensuring a wimp_get_window_outline */ /* call didn't return an error - the code above executes */ /* if there was no error. */ } /* Closure of 'for' looping round all children */ } /* Closure of long 'if' checking that the frameset has spacing */ /* between frames. The code above executes if so. */ } /* Closure of first part of long 'if' checking if the browser */ /* has children. The code above executes if so, the code */ /* below executes if not. */ } /* If the browser doesn't have child frames, want to draw the document it holds. */ else { #ifdef TRACE if (tl & (1u<<9)) Printf("redraw_draw: Have no children\n"); #endif if (b->stream) { #ifdef TRACE if (tl &512) Printf("redraw_draw: Have a document\n"); #endif /* If we've not been told not to plot any backgrounds... */ if (!noback) { /* Then if the plain background flag is set, 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->plainback || !image_tile_window(b, r, 0, toplevel ? -toolbars_button_height(b) - toolbars_url_height(b) : 0) ) { 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 // else // { // /* If plotting no specific backgrounds, show the default one. */ // /* If printing, assume we want to plot no background at all. */ // // if (!printing) // { // if (b->sourcecolours) redraw_set_colour(redraw_backcol(b)); // else redraw_set_colour(choices.col_back); // bbc_rectanglefill(sbox.xmin, sbox.ymin, sbox.xmax - sbox.xmin + 4,sbox.ymax - sbox.ymin + 4); // } // } /* For each line that is present in the browser window... */ 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) { /* If the line would only partially fit on the page, and */ /* we're printing, return it's top coordinate (i.e., if */ /* it falls off the bottom). Note that if printing = 2, */ /* this will not be done (so the printing routine can */ /* signal not to split the line in this way). */ if (osyorg + d->ldata[l].y < page_bottom) { r->xscroll = osyorg + d->ldata[l].y + d->ldata[l].h; return NULL; } } /* 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); y = coords_y_toscreen(0, r); x = coords_x_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->tag == TABLE && ISBODY(tp)) { 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 cx, cy; int cw, ch; int oldback = 0; int oldaa = 0; int oldbgimage = -1; int noback = 1; int padding; BBox rbox; BBox * ibox; int cellindex; int cellcount = 0; int cellmax = table->ColSpan * table->RowSpan; /* Only proceed if there are table cells to redraw */ if (cellarray) { 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]; padding = tables_cell_padding(b, cell); #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->backgroundcol; oldaa = b->aacol; oldbgimage = b->backimage; b->backgroundcol = TD_BGCOL(head); b->aacol = b->backgroundcol; b->backimage = -1; /* For now, no background images in table cells. */ noback = 0; } /* 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--; /* Rounding errors - give some tolerance in all directions */ rbox.xmin -= 4; rbox.ymin -= 4; rbox.xmax += 3; /* (- 1 to make the coordinate inclusive, then + 2) */ rbox.ymax += 3; { int swap; 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; 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 + padding, keepy + cell->y + oh - padding, b, cell, r, 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 (!noback) { b->backgroundcol = oldback; b->aacol = oldaa; b->backimage = oldbgimage; } /* Convert back to OS units ready for drawing the border */ convert_pair_to_os(x + cell->x, y + cell->y + oh - cell->cellheight, &cx, &cy); // cx += 2; // cy -= 2; convert_pair_to_os(cell->cellwidth, cell->cellheight, &cw, &ch); #ifdef TRACE if (tl & (1u<<20)) Printf("cell box at %d %d %d %d\n",cx,cy,cw,ch); #endif /* Draw the slabbed in cell border */ // if (TABLE_BORDER(table)) // { // redraw_set_colour(Redraw_Colour_PlinthGrey); // // bbc_rectanglefill(cx, cy + ch, cw - 1, 1); // bbc_rectanglefill(cx, cy, 1, ch - 1); // // redraw_set_colour(Redraw_Colour_AlmostWhite); // // bbc_rectanglefill(cx, cy, cw - 1, 1); // bbc_rectanglefill(cx + cw, cy, 1, ch - 1); // } /* 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 && ...)' */ } /* Closure of 'if (cellarray)' */ } /* Closure of unconditional code block dealing with redrawing */ /* the body of a table. */ } // /* Draw the slabbed out table border */ // // { // int cx, cy, cw, ch; // // convert_pair_to_os(x, y, &cx, &cy); // // x -= 2; // // convert_to_os(d->cdata[cn].w, &cw); // ch = d->ldata[l].h; // // #ifdef TRACE // if (tl & (1u<<20)) Printf("table box at %d %d %d %d\n",cx,cy,cw,ch); // #endif // // if (TABLE_BORDER((table_stream *) tp)) // { // redraw_set_colour(Redraw_Colour_AlmostWhite); // // bbc_rectanglefill(cx, cy + ch, cw - 1, 1); // bbc_rectanglefill(cx, cy, 1, ch - 1); // // redraw_set_colour(Redraw_Colour_PlinthGrey); // // bbc_rectanglefill(cx, cy, cw - 1, 1); // bbc_rectanglefill(cx + cw, cy, 1, ch - 1); // } // } } /* Deal with forms elements */ if (tp->style & (INPUT | TEXTAREA | SELECT)) { /* A text-based element */ if ( (tp->style & (TEXTAREA | 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); fm_font_box(fh, &box); /* Set up the bounding box for a text area, with a minimum of 3 rows */ if (tp->style & TEXTAREA) { int r; r = tp->rows; if (r < 2) r = 2; box.ymax = (box.ymax - box.ymin + 4) * r + box.ymin; } 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->style & SELECT)); fm_set_font_colour(fh, redraw_token_colour(b, tp), (tp->style & SELECT) ? Redraw_Colour_BackGrey : Redraw_Colour_White); form_textarea_redraw(b, d->cdata[cn].t, &box, &r->redraw_area, fh, !!(tp->style & TEXTAREA), (tp->style & INPUT) && HtmlINPUTtype(tp) == inputtype_PASSWORD); } /* If the element is a SELECT field, it needs a menu icon too */ if (tp->style & 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->selecol); coords_box_toworkarea(&icon, r); if (nocontent != tp) { WimpPlotIconBlock block; block.bbox = icon; block.flags = 0x1700311B; if (redraw_backcol(b) != Redraw_Colour_BackGrey) block.flags |= (1<<2); /* Border if not using Wimp grey background */ block.data.ist.buffer = ""; block.data.ist.validation = "Sfgright"; block.data.ist.buffer_size = 4; wimp_plot_icon(&block); } // sprite_id sid; // sprite_info info; // // sid.tag=sprite_id_name; // sid.s.name="fgright"; // if(!sprite_readsize(resspr_area(),&sid,&info)) // { // int w,h; // // w=info.width<<bbc_modevar(info.mode,bbc_XEigFactor); // h=info.height<<bbc_modevar(info.mode,bbc_YEigFactor); // wimp_setcolour(7); // bbc_rectanglefill(box.xmax-w-8,box.ymin+4,w+8-1,h+4-1); // plotspr_plot(resspr_area(),&sid,8,box.xmax-w-4,box.ymin+4,NULL); // } } } 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; case inputtype_HIDDEN: break; case inputtype_SUBMIT: /* SUBMIT same as RESET: 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); 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, temp, 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, &temp, &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: o = 0; convert_pair_to_os(x, y + base, &ox, &oy); /* 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(ox, r), coords_y_toworkarea(oy, r)); if (!reformat_get_image_size(b, tp, &box)) { ox -= box.xmin; /* Draw a border of tp->maxlen * 2 OS units width around an */ /* image, if it represents a link. (I.e. if 'border="3"' */ /* was specified in the document source, a 3 pixel wide */ /* border would be drawn in mode 20, say). */ box.xmin += ox; box.ymin += oy; box.xmax += ox; box.ymax += oy; if (ISLINK(tp)) { o = (tp->style & IMG) ? tp->maxlen * 2 : 4; 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->selecol); } else if ((tp->style & INPUT) && redraw_selected(b, tp)) redraw_border_around_box(&box, b->selecol); /* Redraw the image itself */ if (nocontent != tp) { e = image_redraw(b, r, d->cdata[cn].t, o + box.xmin, o + box.ymin); if (e) return e; } } } /* Plot a horizontal rule */ else if (tp->style & HR) { int w, h, available, ox, rmarg, lmarg, oy = 0; convert_to_os(y, &oy); available = redraw_display_width(b, d); /* Work out margins */ lmarg = redraw_margin(b, d, tp); if (tp->style & BLOCKQUOTE) rmarg = lmarg; else rmarg = redraw_right_margin(b, d); convert_pair_to_os(lmarg, rmarg, &lmarg, &rmarg); /* In (for example) Netscape Navigator (TM), a 100% width HR */ /* will only span between the left and right margins. So */ /* want to reduce apparent available space by this amount. */ available -= (lmarg + rmarg); if (available < 0) available = 0; /* Deal with a width specifier */ if (HR_HAS_WIDTH(tp)) { /* Currently recognise pixels and percentages */ switch (HR_WIDTH_UNITS(tp)) { case UNITS_PIXELS: w = HR_WIDTH(tp) * 2; break; case UNITS_PERCENT: w = available * HR_WIDTH(tp) / 100; break; default: w = available; break; } } else w = available; /* 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; oy += 6; if (HR_ALIGN(tp) == ALIGN_LEFT || w > available) ox = lmarg; else if (HR_ALIGN(tp) == ALIGN_RIGHT) ox = redraw_display_width(b, d) - rmarg - w; else ox = ((redraw_display_width(b, d) - w) >> 1); ox = (coords_x_toscreen(ox + 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 = fetch_token_data_address(b,d->cdata[cn].t); if (dp) { int c; /* Find the font handle for the token, and its colour */ h = fm_find_token_font(b, tp); c = redraw_token_colour(b, tp); // Printf("h: %d, c: %d, string: '%s'\n",h,c,dp + d->cdata[cn].o); /* Set the font colour and plot the text */ fm_set_font_colour(h,c,redraw_background_colour(b,c)); // Printf("x: %d, y: %d, base: %d\n",x,y,base); if (dp) fm_putsl(h, x, y + base, dp + d->cdata[cn].o, d->cdata[cn].l, 0, b->backimage >= 0 && !b->plainback); if (ISLINK(tp) && !(tp->tag == TABLE && ISBODY(tp))) { /* Underline the item - set the colour, and start at the item's x coordinate... */ int ox,oy; if (b->underlinelks) { redraw_set_colour(redraw_token_colour(b, tp)); convert_pair_to_os(x, y + base, &ox, &oy); bbc_move(ox,oy - 7); /* ...finish at x plus its width. */ convert_pair_to_os(x + d->cdata[cn].w, y + base, &ox, &oy); bbc_draw(ox,oy - 7); } } // fm_lose_font(b, h); } } // 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. */ } /* 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) { /* Set the graphics background colour to the default */ /* and clear the graphics rectangle [to this colour]. */ redraw_set_colour(choices.col_back); 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; BBox size; /* Claim a font */ h = fm_find_font(b, "sans", (int) (choices.fontsize * 1.5), (int) (choices.fontsize * 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 */ y = coords_y_toscreen(-toolbars_button_height(b) - toolbars_url_height(b) - size.ymax - size.ymin - 40, r); /* 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: 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.col_text, choices.col_back); /* Write the string */ fm_puts(h, x, y, lookup_token("NoData:The server returned a blank page.", 0, 0), 1, 1); // fm_lose_font(b, h); 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); return NULL; } /*************************************************/