/* 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 : Reformat.c */ /* */ /* Purpose: Functions to handle page reformatting. */ /* */ /* Author : A.D.Hodgkinson */ /* */ /* History: 03-Dec-96: Created */ /* 16-Apr-97: First merge with T.Cheal's */ /* table code... */ /* 22-May-97: Amazingly, *still* trying */ /* to get this to work. */ /* 18-Jun-97: Hpmh; works, but very slow. */ /* Will need to rewrite at */ /* some stage; for the moment, */ /* moved a few bits over to */ /* Tables.c as they fitted in */ /* better over there. */ /***************************************************/ #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" /* (Which itself includes URLstat.h) */ #include "FetchPage.h" #include "FontManage.h" #include "Forms.h" #include "History.h" #include "Images.h" #include "Memory.h" #include "Redraw.h" #include "Tables.h" #include "Toolbars.h" #include "Reformat.h" /* Local constant definitions */ #define BULLET_GAP 12 /* Statics */ static int reformat_istext (HStream * tp); static int reformat_useless_token (HStream * tp); static int reformat_newline_check (HStream * current, HStream * last, int offset); static int reformat_datasize (HStream * p); static _kernel_oserror * reformat_token_width (reformat_width_data * w, unsigned int flags); static void reformat_check_visited (browser_data * b, HStream * token); static _kernel_oserror * reformat_add_line (browser_data * b, reformat_cell * cell); static _kernel_oserror * reformat_add_line_chunk (browser_data * b, reformat_cell * cell); static _kernel_oserror * reformat_format_from_now (browser_data * b, int lastline); static _kernel_oserror * reformat_check_height (int toplevel, browser_data * b, reformat_cell * d, int line, HStream * tp, HStream * tpLast, int offset); static int reformat_reformatter_r (unsigned int flags, browser_data * b, reformat_cell * d, HStream * streambase); // static void reformat_strip_prespace (browser_data * b, int chunk, HStream * tp); /*************************************************/ /* reformat_formatting() */ /* */ /* Returns 0 if reformatting is not in progress, */ /* else non-zero. */ /* */ /* Parameters: Pointer to the browser_data */ /* structure associated with the */ /* page which might be reformatting. */ /* */ /* Returns: 0 if reformatting is not in */ /* progress, else it is. */ /*************************************************/ int reformat_formatting(browser_data * b) { // if (b->suspend_format) Printf("reformat_formatting: %p suspend_format set\n",b); // else if (b->last_token) // { // if (!b->last_token->next) Printf("reformat_formatting: %p b->last_token->next = NULL\n",b); // else if (b->last_token->next && !(b->last_token->next->flags & HFlags_DealtWithToken)) Printf("reformat_formatting: %p last_token->next HFlags NULL\n",b); // else if (b->cell->nlines <= 0) Printf("reformat_formatting: %p will return b->final_token != NULL: %d\n",b,b->final_token != NULL); // else Printf("reformat_formatting: %p will return 1\n",b); // } // else if (b->cell->nlines <= 0) Printf("reformat_formatting: %p will return b->final_token != NULL: %d\n",b,b->final_token != NULL); // else Printf("reformat_formatting: %p will return 1\n",b); /* Reformatting had been suspended due to an error */ if (b->suspend_format) return 0; if (b->last_token) { /* If we've not overrun the tokens dealt with by the fetcher, */ /* the fetcher could have exitted and this could be the last */ /* token (so we've finished). */ /* */ /* The check for a 'next' token is done as final_token only */ /* records main token stream tokens dealt with by the */ /* preprocessor. It is possible for a precise set of */ /* circumstances to leave last_token and final_token */ /* equivalent due to table parsing, though there is more data */ /* in the main token stream after it (this happened during */ /* testing, so it isn't all that rare). */ if (!b->last_token->next) return 0; /* If the token after the last one dealt with by the */ /* fetcher had not been looked at by the preprocessor, */ /* the reformatter must have exitted for that reason. */ if (b->last_token->next && !(b->last_token->next->flags & HFlags_DealtWithToken)) return 0; } /* If there are no lines, are there any HStream structures? */ /* If so, the preprocessor has got stuff from the fetcher */ /* but the reformatter hasn't built anything with them yet. */ if (b->cell->nlines <= 0) return (b->final_token != NULL); /* If none of the above, we must still be formatting */ return 1; } /*************************************************/ /* reformat_stop() */ /* */ /* Suspends a reformat, flagging that this has */ /* been done in the browser_data structure. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* with the details of the reformat */ /* process to stop inside it. */ /*************************************************/ _kernel_oserror * reformat_stop(browser_data * b) { b->suspend_format = 1; return toolbars_set_button_states(b); } /*************************************************/ /* reformat_istext() */ /* */ /* Returns 1 if an HStream structure represents */ /* neither a horizontal rule nor an image. */ /* */ /* Parameters: Pointer to the HStream structure. */ /* */ /* Returns: 1 if the struct represents text, */ /* else 0. */ /*************************************************/ static int reformat_istext(HStream * tp) { return ( ((tp->style) & (IMG | HR)) == 0 && !(tp->tag == TABLE) && ! ( tp->tagno == TAG_INPUT && HtmlINPUTtype(tp) == inputtype_IMAGE ) ); } /*************************************************/ /* reformat_token_width() */ /* */ /* Fills in the 'width' and 'bytes' fields of a */ /* reformat_width_data structure according to */ /* the contents of the token (HStream structure) */ /* that the reformat_width_data structure points */ /* to (this structure is defined in Reformat.h). */ /* */ /* The idea is to fit the token into maxwidth */ /* coordinates (in millipoints). The actual */ /* width is returned (which may be greater than, */ /* or less than maxwidth) and the number of */ /* bytes used to make that width is returned. */ /* */ /* Parameters: Pointer to a reformat_width_data */ /* structure (see Reformat.h); */ /* */ /* A flags word, as ppassed to */ /* reformat_reformatter. */ /* */ /* Returns: Fills in the structure's 'width' */ /* and 'bytes' fields with width and */ /* byte count information. */ /*************************************************/ static _kernel_oserror * reformat_token_width(reformat_width_data * w, unsigned int flags) { _kernel_oserror * e = NULL; BBox box; /* Deal with tables */ if (w->tp->tag == TABLE && ISBODY(w->tp)) { reformat_cell * cellarray; table_stream * table = (table_stream *) w->tp; int size; int toplevel; #ifdef TRACE if (tl & (1u<<20)) Printf("reformat_token_width: Dealing with table, token %p\n",table); #endif tables_count_table(w->b, table); #ifdef TRACE if (tl & (1u<<20)) Printf("reformat_token_width: Tag is now 0x%x\n",w->tp->tag); #endif tables_position_table(w->b, table); #ifdef TRACE if (tl & (1u<<20)) Printf("reformat_token_width: Tag is now 0x%x\n",w->tp->tag); #endif size = table->ColSpan * table->RowSpan; #ifdef TRACE if (tl & (1u<<20)) Printf("reformat_token_width: Size of table is %d\n",size); #endif /* If reformatting, the table structure's 'cells' field will */ /* already have information attached, so free this first. */ if (table->cells) HtmlFree(table->cells); /* Now allocate a new cell array and initialise the cell contents */ table->cells = cellarray = HtmlMalloc(size * sizeof(reformat_cell), table); /* If the allocation fails, can't possibly continue so jump */ /* back to poll loop after reporting the error. */ if (!table->cells) show_error_cont(make_no_table_memory_error(9)); /* Otherwise, initialise the cell contents */ tables_init_table(w->b, table, cellarray); /* Find the width and height of the cells and fix their positions */ /* as millipoint offsets from the top left of the table. */ /* */ /* If the reformatter flags say not to generate lines, then we */ /* must be doing a width finding session as part of a parent */ /* table, so toplevel is 0. Otherwise, this is the highest level */ /* and toplevel is therefore set to 1. */ if (flags & Reformatter_Virtual) toplevel = 1; else toplevel = 0; w->width = tables_width_table (toplevel, w->b, table, w->maxwid, cellarray); // + 3200 * (table->ColSpan + 1); //3200 for edge grooves w->bytes = tables_height_table(toplevel, w->b, table, cellarray); // + 3200 * (table->RowSpan + 1); //3200 for edge grooves tables_fix_table(w->b, table, cellarray); #ifdef TRACE if (tl & (1u<<20)) Printf("reformat_token_width: Tag is now 0x%x\n",w->tp->tag); #endif /* Return the cell array pointer for convenience (may prove useful */ /* to have this in the reformatter some time...). */ w->data = (char *) cellarray; return e; } /* Deal with forms: Text-based elements */ else if ( w->tp->tagno == TAG_SELECT || w->tp->tagno == TAG_TEXTAREA || w->tp->tagno == TAG_INPUT ) { if ( w->tp->tagno == TAG_SELECT || w->tp->tagno == TAG_TEXTAREA || HtmlINPUTtype(w->tp) == inputtype_PASSWORD || HtmlINPUTtype(w->tp) == inputtype_TEXT ) { int h, length; int extra = 0; int done = 0; if (w->tp->tagno == TAG_TEXTAREA) { /* Text areas */ length = w->tp->cols; if (length == 1) length = 2; else if (length < 2) length = 30; done = 0; } else if (w->tp->tagno == TAG_SELECT) { /* SELECT elements (have pop-up menus) */ int width, l, temp; char * p; e = read_sprite_size("fgright", &width, NULL); if (e) return e; width += 32; /* Account for border and gap */ convert_to_points(width, &extra); /* Need to find out the actual used width, or we end up */ /* generally overestimating the required size. */ p = (char *) HtmlSELECToptions(w->tp) + 8; h = fm_find_token_font(NULL, w->tp); l = 0; width = 0; length = 0; /* Not used, but stops compiler giving warnings */ /* Some fields can hold the selNONE or selMANY text, as well */ /* as the menu item texts. */ if (HtmlSELECTmultiple(w->tp) || *p == 0xff) { e = fm_get_string_width(h, lookup_token("selNONE:<None>",0,0), Reformat_AsWideAsPossible_MP, strlen(tokens), -1, &temp, &width); if (!e && width > l) l = width; } if (HtmlSELECTmultiple(w->tp)) { e = fm_get_string_width(h, lookup_token("selMANY:<Many>",0,0), Reformat_AsWideAsPossible_MP, strlen(tokens), -1, &temp, &width); if (!e && width > l) l = width; } /* Look for the width of the widest OPTION field */ while (*p != 0xff) { p++; e = fm_get_string_width(h, p, Reformat_AsWideAsPossible_MP, strlen(p), -1, &temp, &width); if (e) { erb = *e, e = &erb; width = 16000; /* Arbitrary! Give it 40 OS units on error */ break; } /* Record if this is the widest and skip to the next field */ if (width > l) l = width; p += strlen(p) + 1; p += strlen(p) + 1; } /* Set the width and flag that we've done the calculation */ /* already so code lower down doesn't try. */ w->width = l + extra; done = 1; fm_lose_font(NULL, h); } else { /* One line writable */ length = HtmlINPUTsize(w->tp); if (length == 1) length = 2; else if (length < 2) { length = w->tp->maxlen; if (length > 40) length = 40; } done = 0; } /* Some cases above may have worked out the width already. */ /* For those that haven't, do it on the basis of 'length', */ /* holding the maximum number of characters, and the */ /* width of the widest character possible in the item. */ if (!done) { if (length == 1) length = 2; else if (length < 2) length = 20; /* Arbitrary adjustment for proportional fonts */ if ((!choices.system_font) && !(w->tp->style & (PRE | TT))) length /= 2; h = fm_find_token_font(NULL, w->tp); e = fm_font_box(h, &box); if (e) erb = *e, e = &erb; convert_box_to_points(&box, &box); w->width = (((box.xmax - box.xmin) * length)) + 16 * 400 + extra; fm_lose_font(NULL, h); } w->bytes = 0; return e; } else switch(HtmlINPUTtype(w->tp)) { case inputtype_SUBMIT: /*; no break - same as RESET */ case inputtype_RESET: { const char * p; int h, length, end; p = form_button_text(w->tp); length = strlen(p); end = 0; while(end < length && p[end] != '\n') end++; h = fm_find_token_font(NULL, w->tp); e = fm_get_string_width(h, p, Reformat_AsWideAsPossible_MP, end - w->offset, -1, &w->bytes, &w->width); if (e) erb = *e, e = &erb; w->width += 400 * 4 * 12; /* Account for border */ w->bytes = 0; fm_lose_font(NULL, h); return e; } break; case inputtype_CHECKBOX: { w->bytes = 0; read_sprite_size("fopton", &w->width, NULL); convert_to_points(w->width, &w->width); } break; case inputtype_RADIO: { w->bytes = 0; read_sprite_size("fradioon", &w->width, NULL); convert_to_points(w->width, &w->width); } break; case inputtype_HIDDEN: { w->width = w->bytes = 0; } break; case inputtype_IMAGE: { goto do_image; /* See below */ } break; } } else if (w->tp->style & IMG) { do_image: /* Used by switch statement above */ w->bytes = 0; w->width = 0; /* If the image has a known width and height, the reformatter has */ /* dealt with it - so the image library can mark it as redrawable */ /* now (otherwise, for delayed reformats, some images which were */ /* cross referenced and did not have reformat sessions explicitly */ /* started for them, may get stuck in a 'non-redrawable' state). */ image_token_check_redrawable(w->b, w->tp); /* Now get the size of the image for reformatting purposes */ e = reformat_get_image_size(w->b, w->tp, &box); if (e) return e; w->width = box.xmax - box.xmin; convert_to_points(w->width, &w->width); } else if (w->tp->style & HR) { /* For a horizontal rule, there's no extra data to put in (so */ /* the w->bytes field is zero) and the width is as wide as */ /* the specified maximum width. */ /* */ /* The special case of finding min/max widths is checked in */ /* the flags here, as otherwise any HR tag can force the */ /* width up to maxwid - which may be very large. */ w->bytes = 0; if ( !(flags & Reformatter_FindingWidest) && !(flags & Reformatter_FindingSmallest) ) w->width = w->maxwid; else w->width = 0; } else if (ISBULLET(w->tp)) /* ISBULLET is defined in Fetch.h */ { /* For a bullet, there is again no extra data so bytes = 0, */ /* and the width is taken from the sprite width of the */ /* bullet point. */ w->bytes = 0; convert_to_points(reformat_bullet_width(w->tp->indent), &w->width); } else { int h, end, split; /* Can't do anything if there's no text */ if (!w->data || !*w->data) { w->bytes = 0; w->width = 0; } else { /* Loop round until a newline is found, or the end of the */ /* string is reached, starting at the offset into the data */ /* specified by the 'offset' field. */ end = w->offset; while (w->data[end] && w->data[end] != '\n') end++; /* If the text is preformatted, set a null split character. */ /* Else specify splitting on spaces. */ split = (w->tp->style & PRE) ? -1 : ' '; /* Get a font handle for rendering the token */ h = fm_find_token_font(NULL, w->tp); /* If end > offset, the loop above must have gone through at least */ /* one non-newline character in the string, or there was no string */ /* to look through; find the width of the string (the call won't */ /* mind if there's no string, it'll just return 0) */ if (end > w->offset) e = fm_get_string_width(h, w->data + w->offset, w->maxwid, end - w->offset, split, &w->bytes, &w->width); /* If finding the minimum or maximum width for tables, add a little to */ /* the above width to account for italics etc. - the font manager will */ /* not have returned the upper limit on either side. */ if ( (flags & Reformatter_FindingWidest) || (flags & Reformatter_FindingSmallest) ) w->width += 3200; /* If the scan for a newline finished before the end of the string (so */ /* a newline was found) and the chunk of data defined by the call to */ /* fm_get_string_width above gave a string ending in a newline, add 1 */ /* to the bytes counter to ensure that the chunk includes it. */ if (w->data[end] && w->data[w->offset + w->bytes] == '\n') w->bytes++; /* We don't need to keep the font claimed for just finding out a width */ fm_lose_font(NULL, h); } } return e; } /*************************************************/ /* reformat_useless_token() */ /* */ /* Checks to see if a token (HStream struct) is */ /* of any use to the reformatter. */ /* */ /* Parameters: Pointer to the HStream struct. */ /* */ /* Returns: 1 = the token is useless, else 0. */ /*************************************************/ static int reformat_useless_token(HStream * tp) { if (ISHEAD(tp)) return 1; if (ISNULL(tp)) return 1; if ( ISBODY(tp) && !tp->tag && !tp->text && !tp->anchor && !tp->src && !tp->enctype && !tp->name && !tp->value && !tp->target && ( tp->style == 0x00000000 || tp->style == 0x80000000 ) ) return 1; return 0; } /*************************************************/ /* reformat_newline_check() */ /* */ /* Works out whether or not there should be a */ /* line break in the page, according to the */ /* token (HStream structure) that is currently */ /* being considered and the token that was last */ /* considered, and the offset into the data of */ /* the tokens. */ /* */ /* Parameters: Pointer to the current token (the */ /* HStream structure that is being */ /* dealt with by the reformatter, */ /* say, at the moment); */ /* */ /* Pointer to the last token that */ /* was dealt with; */ /* */ /* Data offset into the tokens. */ /* */ /* Returns: 0 if there is no line break */ /* needed, or a value between 1 and */ /* 8 that says 'yes, line break */ /* needed' and also holds details of */ /* the conditions that were met to */ /* determine the line break was */ /* needed. */ /*************************************************/ static int reformat_newline_check(HStream * current, HStream * last, int offset) { /* If the current token represents a horizontal rule and the last token also */ /* represented one, with no offset indicating extra data in the tokens (and */ /* provided that there is actually a last token!), then return 1. */ if (((current->style) & HR) || ((!offset) && (last) && ((last->style) & HR))) return 1; /* If the tag itself indicates that a linebreak is needed, and we are at the */ /* start of this line (offset into it is zero), return 2. This may be due to */ /* <P>, <BR> and the like, in the document source. */ if ((!offset) && ((current->style) & LINEBREAK)) return 2; /* If, again, this is the start of a line, and the pointer to the previous */ /* token dealt with is not null, proceed with other line break checks. */ if ((!offset) && (last)) { /* If the indentation has changed since the last token (e.g. a new list has */ /* been started or an old one closed between the two tokens) then return 3. */ if (current->indent != last->indent) return 3; /* If the header type changed between the two tokens, then provided the */ /* last token wasn't a list item (in which case a line break will already */ /* have been put in) return 4. */ if (((current->style & H_MASK) != (last->style & H_MASK)) && !(last->style & LI)) return 4; /* When certain tags are turned on or off, we want a linebreak. For example */ /* it looks tidier for preformatted text to have a gap above and below it, */ /* rather than having it touch the previous and following text. The macro */ /* LINEBREAKSW (defined in Reformat.h) has a list of these tags ORed */ /* together to form a mask - if this mask changes between the tokens, one */ /* of the listed tags must have turned on or off. So we want a line break; */ /* hence, return 5. */ if ((current->style & LINEBREAKSW) != (last->style & LINEBREAKSW)) return 5; /* CENTRED is defined in Reformat.h, and is 1 if the token holds data that */ /* should be displayed centred in the page. If switching from centred to */ /* uncentred text or vice versa, there must be a line break. So return 6. */ if (CENTRED(current) != CENTRED(last)) return 6; /* Some tags need a line break when they turn on, for example at the start */ /* of certain special kinds of list. The LINEBREAKON macro is defined in */ /* reformat.h and contains an ORed together list of such tags (just like */ /* the LINEBREAKSW macro used above) - so if the current token has one of */ /* these turned on, but the last token had it turned off, we want another */ /* line break; return 7. */ if ((current->style & LINEBREAKON) && !(last->style & LINEBREAKON)) return 7; /* Similarly, some tags need a line break when they turn off; for example, */ /* headers look better if they have a gap underneath them. So again, there */ /* is a macro (LINEBREAKOFF) in Reformat.h which has a list of these tags */ /* and a value of 8 is returned if we go from one of these being turned */ /* on to one being turned off. */ if (!(current->style & LINEBREAKOFF) && (last->style & LINEBREAKOFF)) return 8; } /* If none of the above conditions are met, we need no line break - return 0. */ return 0; } /*************************************************/ /* reformat_newline() */ /* */ /* Returns 1 if a line break should be inserted */ /* onto the page, or 0 if not. Does this by */ /* calling reformat_newline_check and is only */ /* interested in if the value the call returned */ /* was zero or not. */ /* */ /* Parameters: As for reformat_newline_check. */ /* */ /* Returns: 1 = line break required, else 0. */ /*************************************************/ int reformat_newline(HStream * current, HStream * last, int offset) { return (reformat_newline_check(current, last, offset) != 0); } /*************************************************/ /* reformat_datasize() */ /* */ /* Returns the size of standard data pointed to */ /* by an HStream (e.g. the length of any text */ /* string it points to). */ /* */ /* Parameters: Pointer to the HStream struct */ /* */ /* Returns: Size of associated data in bytes. */ /*************************************************/ static int reformat_datasize(HStream * tp) { /* Is this a text item? */ if (!reformat_istext(tp)) return 0; /* If the HStream has a pointer to some text, return the */ /* length of that text, else return zero. */ return (tp->text ? strlen(tp->text) : 0); } /*************************************************/ /* reformat_shift_vertically() */ /* */ /* Shifts all lines between two given line */ /* numbers (inclusive) by a given y coordinate, */ /* in OS units. A redraw is generated with */ /* Wimp_BlockCopy if moved lines lie inside the */ /* visible area of the browser window. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* holding information on the lines; */ /* */ /* The line number at which to start */ /* (inclusive); */ /* */ /* The line number at which to end */ /* (inclusive); */ /* */ /* The value to shift the lines by, */ /* in OS units - since the Y coords */ /* of the lines are for a window, */ /* a negative value would move the */ /* lines down the window and vice */ /* versa. */ /*************************************************/ _kernel_oserror * reformat_shift_vertically(browser_data * b, int start, int end, int y_shift) { _kernel_oserror * e; reformat_cell * cell = b->cell; int line; WimpGetWindowStateBlock s; if (!y_shift) return NULL; /* Limit check start and end, and exit if there appear */ /* to be no lines to move */ if (start > cell->nlines - 1) return NULL; if (start < 0) start = 0; if (end > cell->nlines - 1) end = cell->nlines - 1; if (end < 0) end = 0; if (start > end) line = start, start = end, end = line; if ((end < 0) || (start < 0) || (!cell->ldata)) return NULL; /* Alter the lines' y-coordinates */ for (line = start; line <= end; line ++) cell->ldata[line].y += y_shift; /* Have to force a redraw of the entire page to ensure */ /* that image positions are updated */ s.window_handle = b->window_handle; e = wimp_get_window_state(&s); if (e) return e; coords_box_toworkarea(&s.visible_area, (WimpRedrawWindowBlock *) &s); return wimp_force_redraw(s.window_handle, s.visible_area.xmin, s.visible_area.ymin, s.visible_area.xmax, s.visible_area.ymax); } /*************************************************/ /* reformat_format_from() */ /* */ /* Starts a new reformat session, possibly */ /* starting after a given last line (i.e. from */ /* a certain point in the page, rather than the */ /* whole page). */ /* */ /* The reformat session may be postponed until */ /* later according to the RefoTime and RefoWait */ /* entires in the Choices file. */ /* */ /* The defined way to ensure all line and chunk */ /* data for a given browser is freed, including */ /* that tied up in tables, is by calling with */ /* the browser_data struct, -1, 1, and -1 (see */ /* parameters list below). */ /* */ /* Parameters: A pointer to a browser_data */ /* structure for which the reformat */ /* is to take place; */ /* */ /* The number of the last valid */ /* reformat_line structure; */ /* */ /* 1 to format now, else the request */ /* may be delayed for some time; */ /* */ /* For requests generated by the */ /* resizing of images, pass the */ /* image number, else pass -1. */ /*************************************************/ _kernel_oserror * reformat_format_from(browser_data * b, int lastline, int immediate, int image) { _kernel_oserror * e; /* Do we delay these requests? */ if (!choices.refo_wait || immediate) reformat_format_from_now(b, lastline); else { if (b->refo_time) { /* If there's already a pending request, is the line number */ /* specified for this one�lower than the one already there? */ /* If so, then use that higher line, else stick with the */ /* old one. Nothing else to do - the null handler takes */ /* care of it all. */ if (lastline < b->refo_line) b->refo_line = lastline; } else { /* This is the first reformat request; so set the timer and */ /* install a null handler for it. */ e = _swix(OS_ReadMonotonicTime, _OUT(0), &b->refo_time); if (e) return e; b->refo_line = lastline; register_null_claimant(Wimp_ENull, (WimpEventHandler *) reformat_format_timer, b); } } return NULL; } /*************************************************/ /* reformat_format_timer() */ /* */ /* Calls the reformatter after a delay given by */ /* the Choices file entry 'RefoTime', according */ /* to the start time specified by the */ /* 'refo_time' field of the browser_data */ /* structure. */ /* */ /* Parameters are as standard for a Wimp event */ /* handler. */ /*************************************************/ int reformat_format_timer(int eventcode, WimpPollBlock * b, IdBlock * idb, browser_data * handle) { int timenow; /* If there appears to be no initiation time specified in the */ /* browser_data structure, dereigster and exit (being cautions...) */ if (!handle->refo_time) { handle->refo_time = 1; /* So that reformat_stop_pending will deregister this handler */ reformat_stop_pending(handle); return 0; } /* Otherwise, get the current time into 'timenow' */ if (_swix(OS_ReadMonotonicTime, _OUT(0), &timenow)) return 0; /* Do we need to reformat yet? */ if (timenow - handle->refo_time > choices.refo_time) { /* reformat_format_from_now will deregister this handler */ ChkError(reformat_format_from_now(handle, handle->refo_line)); } return 0; } /*************************************************/ /* reformat_format_from_now() */ /* */ /* Starts a new reformat session, possibly */ /* starting after a given last line (i.e. from */ /* a certain point in the page, rather than the */ /* whole page). */ /* */ /* The reformat session will be stared */ /* immediately - it will not be delayed. Don't */ /* call this directly - go through */ /* reformat_format_from instead. */ /* */ /* Parameters: A pointer to a browser_data */ /* structure for which the reformat */ /* is to take place; */ /* */ /* The number of the last valid */ /* reformat_line structure. */ /*************************************************/ static _kernel_oserror * reformat_format_from_now(browser_data * b, int lastline) { int bottom = 0; reformat_cell * cell = b->cell; /* If this browser had any queued reformats, cancel them */ if (b->refo_time) { if (b->refo_line < lastline) lastline = b->refo_line; reformat_stop_pending(b); } /* Line numbers less than -1 are invalid, and can't end */ /* on a line greater than the number of lines present! */ if (lastline < -1) lastline = -1; if (lastline >= cell->nlines) lastline = cell->nlines - 1; /* Clear the flag that says formatting is suspended due to error */ b->suspend_format = 0; /* Clear the field holding the last token number for which */ /* reformatting was definitely completed */ b->last_token = NULL; /* If lastline holds a valid line number, set bottom to the */ /* bottom y coordinate of the last line. Otherwise leave */ /* bottom set at 0 (we're reformatting to no lines). */ if (lastline >= 0) bottom = cell->ldata[lastline].y; /* If there's existing line data, need to ensure that all */ /* memory allocated in table cells is freed before getting */ /* rid of the lines after lastline. */ tables_free_memory(1, b, cell, lastline + 1); /* If we're reformatting to less than the current number of */ /* lines, 'chop off' those that are left over. */ if ((lastline + 1) < cell->nlines) { int size;//, topline; // /* Before anything else, mark the token currently displayed at the */ // /* top of the page so it can be shown after the reformat. */ // // topline = browser_top_line(b, cell, 0) - 1; // // if (topline >= 0 && (lastline + 1) < topline) // { // b->display_request = cell->cdata[cell->ldata[topline].chunks].t; // b->display_offset = cell->cdata[cell->ldata[topline].chunks].o; // b->display_vscroll = 0; // } /* Size = offset of line chunks for the last line, plus */ /* the number of line chunks times the size of a */ /* chunk. I.e. the offset for the end of the line */ /* chunks for the last line - so size = size of */ /* data that is needed for all the line chunks */ /* with lastline chunks present. */ if (lastline < 0) size = 0; else { /* If it turns out this line has no chunks, need to use the chunk address */ /* and number of chunks for the previous line; unless, of course, this is */ /* line 0, in which case there are no previous lines to refer to. */ if (!cell->ldata[lastline].n) { if (lastline == 0) size = 0; else size = (cell->ldata[lastline - 1].chunks + /* Base array offset into chunks */ cell->ldata[lastline - 1].n) * /* Add number of chunks for this line to get total number of chunks */ sizeof(reformat_line_chunk); /* Multiply by size of a reformat_line_chunk to get total size */ } /* Otherwise, we're OK; the last line has line chunks. */ else size = (cell->ldata[lastline].chunks + cell->ldata[lastline].n) * sizeof(reformat_line_chunk); } /* Clip the number of lines to the new value */ cell->nlines = lastline + 1; /* Make sure that the right amount of memory is allocated */ #ifdef TRACE if (tl & (1u<<12)) Printf("reformat_format_from: Chunk CK_LINE set to %d\n",(lastline + 1) * sizeof(reformat_line)); #endif memory_set_chunk_size(b, cell, CK_LINE, (lastline + 1) * sizeof(reformat_line)); #ifdef TRACE if (tl & (1u<<12)) Printf("reformat_format_from: Chunk CK_LDAT set to %d\n",size); #endif memory_set_chunk_size(b, cell, CK_LDAT, size); } if (!printing) { /* Ensure null events are claimed for the rest of the reformat */ if (!b->fetch_handler) fetchpage_claim_nulls(b); /* Make sure the forms library knows where the caret should be */ form_check_caret(b); /* Update the status bar */ toolbars_update_status(b, Toolbars_Status_Formatting); } /* Redraw the bottom line of the window if not printing */ return (!printing ? browser_update_bottom(b, bottom) : NULL); } /*************************************************/ /* reformat_stop_pending() */ /* */ /* Stops any pending reformats from happening, */ /* deregistering handlers etc. as needed. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the queue. */ /*************************************************/ void reformat_stop_pending(browser_data * b) { if (b->refo_time) deregister_null_claimant(Wimp_ENull, (WimpEventHandler *) reformat_format_timer, b); b->refo_time = 0; b->refo_line = Reformat_UnrealisticallyHighLineNumber; } /*************************************************/ /* reformat_get_image_size() */ /* */ /* Gets a BBox for a specified image in OS */ /* coordinates relative to the font base line */ /* and left hand edge. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image; */ /* */ /* A token address for the image; */ /* Pointer to a BBox in which the */ /* relevant coords are returned. */ /* */ /* Assumes: Pointer to the BBox may not be */ /* NULL. */ /*************************************************/ _kernel_oserror * reformat_get_image_size(browser_data * b, HStream * tp, BBox * box) { _kernel_oserror * e; imgalign align; /* Get the image size from the image library */ e = image_get_token_image_size(b, tp, box); if (e) return e; /* Deal with alignments */ if (tp->style & IMG) /* It'll either be an IMG or an INPUT TYPE=IMAGE item */ { if ((tp->type & TYPE_ALIGN_MASK) == TYPE_MIDDLE) align = imgalign_MIDDLE; else if ((tp->type & TYPE_ALIGN_MASK) == TYPE_TOP) align = imgalign_TOP; else align = imgalign_NONE; } else align = HtmlINPUTalign(tp); switch (align) { case imgalign_MIDDLE: { box->ymin -= box->ymax / 2; box->ymax /= 2; } break; case imgalign_TOP: { box->ymin =- box->ymax; box->ymax = 0; } break; } /* Deal with links - need to account for a border */ /* of maxlen * 2 pixels width. ISLINK is defined */ /* in Fetch.h. */ if (ISLINK(tp) && (tp->style & IMG)) { int b; b = tp->maxlen * 2; box->xmax += b; box->ymax += b; box->xmin -= b; box->ymin -= b; } return NULL; } /*************************************************/ /* reformat_bullet_width() */ /* */ /* Returns the width of a given bullet in OS */ /* units. */ /* */ /* Parameters: The bullet number (in the Sprites */ /* file, bullets are named b1, b2, */ /* ...bn). */ /*************************************************/ int reformat_bullet_width(int bullet) { char spr[32]; int w; sprintf(spr, "b%d\0", (bullet + bullets - 1) % bullets); if (read_sprite_size(spr, &w, NULL)) w = 32; /* See top of the file for BULLET_GAP */ return w + BULLET_GAP; } /*************************************************/ /* reformat_bullet_height() */ /* */ /* Returns the height of a given bullet in OS */ /* units. */ /* */ /* Parameters: The bullet number (in the Sprites */ /* file, bullets are named b1, b2, */ /* ...bn). */ /*************************************************/ int reformat_bullet_height(int bullet) { char spr[32]; int h; sprintf(spr, "b%d\0", (bullet + bullets - 1) % bullets); if (read_sprite_size(spr, NULL, &h)) h = 32; return h; } /*************************************************/ /* reformat_y_offset() */ /* */ /* Returns the y offset from the top of a page, */ /* in OS units, for all lines on that page. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* representing the page. */ /* */ /* Returns: y offset for all the lines on the */ /* page, in OS units, from the top. */ /*************************************************/ int reformat_y_offset (browser_data * b) { int offset; if (!controls.swap_bars) offset = toolbars_button_height(b) + toolbars_url_height(b); else offset = toolbars_status_height(b); /* The '4' accounts for the bottom window frame of the toolbars */ if (offset) offset += wimpt_dy(); if (!b->ancestor) offset += b->leading; /* Only put a gap at the top for base browsers, not for frames */ return -offset; } /*************************************************/ /* reformat_check_height() */ /* */ /* Looks at the contents of a reformat_line */ /* structure and ensures that it's y coordinate, */ /* height and font baselines offset fields are */ /* correctly filled in. */ /* */ /* Parameters: 1 for all external callers, but */ /* will be 0 when it calls itself */ /* internally; */ /* */ /* Pointer to a browser_data struct */ /* relevant to the line; */ /* */ /* Pointer to a reformat_cell struct */ /* that the line lies in; */ /* */ /* Number of the line in to alter; */ /* */ /* Pointer to the token that may */ /* cause the line height to alter; */ /* */ /* Pointer to the last token dealt */ /* with by the reformatter; */ /* */ /* Offset into the current token's */ /* data. */ /* */ /* Returns: Fills in bits of the line struct, */ /* as mentioned above. */ /*************************************************/ static _kernel_oserror * reformat_check_height(int toplevel, browser_data * b, reformat_cell * d, int line, HStream * tp, HStream * tpLast, int offset) { _kernel_oserror * e; int top = 0, bot = 0; /* Find out the height of an image */ if ( (tp->style & IMG) || ( tp->tagno == TAG_INPUT && HtmlINPUTtype(tp) == inputtype_IMAGE ) ) { BBox box; e = reformat_get_image_size(b, tp, &box); if (e) return e; top = box.ymax; bot = -box.ymin; } /* Size of a horizontal rule; the rule is plotted */ /* centred vertically within its bounding box so */ /* there is no need to set both bot and top to */ /* the same value. With top = 0, setting bot is */ /* enough. */ else if (tp->style & HR) { int size; /* Deal with a (vertical) size specifier */ if (HR_HAS_SIZE(tp)) { /* (Only recognise pixels at present) */ switch (HR_SIZE_UNITS(tp)) { case UNITS_PIXELS: size = HR_SIZE(tp) * 2; break; default: size = 4; break; } } else size = 4; if (size < 24) size = 24; bot = size + 4; b->last_space += bot; } /* A few easy to work out forms elements */ else if ( tp->tagno == TAG_INPUT && HtmlINPUTtype(tp) == inputtype_CHECKBOX ) { read_sprite_size("fopton", NULL, &top); top -= 8; bot = 8; } else if ( tp->tagno == TAG_INPUT && HtmlINPUTtype(tp) == inputtype_RADIO ) { read_sprite_size("fradioon", NULL, &top); top -= 8; bot = 8; } /* Height of a bullet point */ else if (ISBULLET(tp)) { top = reformat_bullet_height(tp->indent); } else if (tp->tag == TABLE && ISBODY(tp)) { /* Don't do anything! h is already correct */ tp = tp; /* Make sure the compiler gets the if..else if.. etc. right */ } /* If the above matches aren't found, use a more general routine */ else { int h; int spaced = 0; int fontsize = choices.font_size >> 4; unsigned char add; BBox box; /* Get the bounding box of the font that should be used for the token */ // if (tp->text && *tp->text) { h = fm_find_token_font(b, tp); e = fm_font_box(h, &box); if (e) return e; top = box.ymax; // /* Account for leading. */ // // /* Don't do for bold/italic due to font manager bug... */ // // if (!(tp->style & (ITALIC | BOLD))) bot += b->leading; bot = -box.ymin + b->leading; /* Ensure the bottom line is not negative */ if (bot < 0) bot = 0; } // else top = bot = box.xmin = box.xmax = box.ymin = box.ymax = 0; /* The amount to add to 'top' to give line break effects */ add = (unsigned char) 3 * fontsize; /* If the token represents a line break but there is no extra */ /* data (so this is right at the top of the paragraph) include */ /* a gap for the break. */ if ((!offset) && (tp->style & (P | UL))) { b->last_space += add, spaced = 1; // if (b->last_space <= add) top += add; top += add; } /* Leave a gap above any DT elements */ if (!offset && (tp->style & DT)) { b->last_space += add, spaced = 1; // if (b->last_space <= add) top += add; top += add; } if ((tpLast) && !(tp->style & (DD | DT)) && (tpLast->style & DD)) { b->last_space += top, spaced = 1; // if (b->last_space <= add) top += add; top += add; } // Debug section, useful to keep around in case things go wrong! // // Printf("H_MASK: %d\n",(tp->style & H_MASK)); // if (tp->text) Printf("Text : '%s'\n",tp->text); // else Printf("(No text)\n"); // Printf("Last : %p\n",(void *) tpLast); // Printf("H_MASK: %d\n",(tpLast->style & H_MASK)); // if (tpLast) Printf("Logic : %d\n\n",((tp->style & H_MASK) && !(tpLast->style & H_MASK))); // else Printf("(No last)\n\n"); /* Gap if the indentation level changes */ if ((tpLast) && (tp->indent != tpLast->indent)) { b->last_space += add, spaced = 1; // if (b->last_space <= add) top += add; top += add; } /* Gaps for headers; first, if a header is turned on, */ /* then, if it is turned off. */ if ((tpLast) && ((tp->style & H_MASK) && !(tpLast->style & H_MASK))) { b->last_space += add, spaced = 1; // if (b->last_space <= add) top += add; top += add; } if ((tpLast) && ((tp->style & H_MASK) && !(tpLast->style & H_MASK))) { b->last_space += add, spaced = 1; // if (b->last_space <= add) bot += add; top += add; } /* Similarly give extra gaps around header entries */ switch (redraw_header(tp->style)) { case 1: bot += 16, top += 20; break; case 2: bot += 16, top += (64 & (!offset)); break; case 3: bot += 16, top += (64 & (!offset)); break; case 4: bot += 16, top += (32 & (!offset)); break; case 5: top += (32 & (!offset)); break; case 6: top += (4 & (!offset)); break; /* Give a gap for block quotations as they switch on or off (i.e. the flag saying */ /* if the tag is part of a block quote has changed between this tag and the last */ /* one). */ default: if (tpLast && ((tp->style & BLOCKQUOTE) != (tpLast->style & BLOCKQUOTE)) && !offset && !add) { b->last_space += top, spaced = 1; // if (b->last_space <= add) top += top; } } if (!spaced) b->last_space = 0; } /* Round up top and bot to a multiple of 4 */ if (top & 3) top += 4 - (top & 3); if (bot & 3) bot += 4 - (bot & 3); /* Work out height of various forms elements */ if (tp->tagno == TAG_TEXTAREA) { /* Text areas, based on the number of rows */ int r; r = tp->rows; if (r < 2) r = 2; top = (top + bot) * r + 12 - bot; bot += 16; } else if (tp->tagno == TAG_SELECT) { /* Selection lists - a pop-up menu icon */ int h; if (read_sprite_size("fgright", NULL, &h)) h = 44; bot += 8; top += 8; if (top + bot < h) top += h - top - bot; } else if (tp->tagno == TAG_INPUT) { /* General input types */ switch(HtmlINPUTtype(tp)) { case inputtype_TEXT: /* No break - same as PASSWORD */ case inputtype_PASSWORD: bot += 8; top += 8; break; case inputtype_SUBMIT: /* No break - same as RESET */ case inputtype_RESET: bot += 8; top += 12; break; } } /* lp->h - lp->b equates to the height of the line in OS units minus */ /* the y offset of the font baseline from the bottom of the line. If */ /* 'top' is greater than this the line needs to grow vertically. */ if (top > (d->ldata[line].h - d->ldata[line].b)) { int diff; diff = top - (d->ldata[line].h - d->ldata[line].b); d->ldata[line].h += diff; d->ldata[line].y -= diff; } /* Similarly, if bot is greater than the offset of the baseline from */ /* the bottom of the line, account for the extra offset. */ if (bot > d->ldata[line].b) { int diff; diff = bot - d->ldata[line].b; d->ldata[line].h += diff; d->ldata[line].b += diff; /* Was lp->b = bot; */ d->ldata[line].y -= diff; } return NULL; } /*************************************************/ /* reformat_check_visited() */ /* */ /* Looks at a given token and compares it to the */ /* global history, setting a bit in the flags */ /* word if a link it represents has been visited */ /* before. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the token; */ /* */ /* Pointer to the token. */ /*************************************************/ static void reformat_check_visited(browser_data * b, HStream * token) { if (ISLINK(token) && history_visited(token->anchor, 0) >= 0) token->flags |= HFlags_LinkVisited; } /*************************************************/ /* reformat_check_extent() */ /* */ /* Sets the browser window vertical extent to */ /* match the page, by looking at the last line */ /* in the line list. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* holding details of the window to */ /* alter. */ /*************************************************/ _kernel_oserror * reformat_check_extent(browser_data * b) { return reformat_set_extent(b, -reformat_return_extent(b, NULL)); } /*************************************************/ /* reformat_return_extent() */ /* */ /* Returns the height of a page in OS units, */ /* according to its current formatted state. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* holding details of the page; */ /* */ /* Pointer to a reformat_cell struct */ /* holding redraw information. */ /*************************************************/ int reformat_return_extent(browser_data * b, reformat_cell * cell) { int extent = 0; if (!cell) cell = b->cell; if (cell->ldata) extent = -cell->ldata[cell->nlines - 1].y; return extent; } /*************************************************/ /* reformat_set_extent() */ /* */ /* Sets the browser window vertical extent. The */ /* extent can only ever grow. */ /* */ /* Parameters: A pointer to a browser_data */ /* structure containing details of */ /* the window to alter; */ /* */ /* A new vertical extent in OS units */ /* - this is a *negative* number, */ /* expressed as a downward offset */ /* from the top of the window. */ /*************************************************/ _kernel_oserror * reformat_set_extent(browser_data * b, int y_extent) { _kernel_oserror * e; WimpGetWindowStateBlock s; BBox new_extent; int hbot, width; #ifdef TRACE if (tl & (1u<<8)) Printf("\nreformat_set_extent: Called, -y_extent = %d\n",-y_extent); #endif /* For windows with frames, don't want to mess around with the extent */ if (b->nchildren) return NULL; s.window_handle = b->window_handle; RetError(wimp_get_window_state(&s)); /* Y extent is set to the requested value plus an amount for the toolbars */ /* and an extra amount for aesthetics. */ if (!controls.swap_bars) hbot = toolbars_status_height(b); else hbot = toolbars_button_height(b) + toolbars_url_height(b); y_extent -= (hbot + b->leading); /* Ensure that the extent is at least as great as the minimum height */ if ((-y_extent) < b->min_height) y_extent = -(b->min_height); /* For the height, don't want to resize below the current */ /* visible height. Things are a bit messy due to the */ /* negative signs on extent, etc. */ if ((-y_extent) < (s.visible_area.ymax - s.visible_area.ymin)) y_extent = -(s.visible_area.ymax - s.visible_area.ymin); new_extent.ymax = 0; new_extent.ymin = y_extent; /* x extent must only match or be larger than the visible */ /* area, never smaller. */ width = s.visible_area.xmax - s.visible_area.xmin; if (b->display_width < width) b->display_width = width; if (b->display_extent < b->display_width) b->display_extent = b->display_width; new_extent.xmin = 0; new_extent.xmax = b->display_extent; /* Set the extent */ RetError(window_set_extent(0,b->self_id,&new_extent)); /* Can't set extent so visible area is now outside the work area; */ /* call wimp_open_window to make sure scroll positions are OK. */ { ObjectId po; ComponentId pc; RetError(toolbox_get_parent(0, b->self_id, &po, &pc)); return toolbox_show_object(0, b->self_id, 1, (void *) &s.visible_area, po, pc); } } /*************************************************/ /* reformat_add_line() */ /* */ /* Adds a line to the array of line structures. */ /* The contents are NOT initialised. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the page; */ /* */ /* Pointer to the reformat_cell */ /* structure to add the chunks to. */ /*************************************************/ static _kernel_oserror * reformat_add_line(browser_data * b, reformat_cell * cell) { _kernel_oserror * e; #ifdef TRACE if (tl & (1u<<8)) Printf("reformat_add_line: Called with cell %p, cell->nlines = %d\n",cell,cell->nlines); #endif /* Allocate memory for the new number of lines */ #ifdef TRACE if (tl & (1u<<12)) Printf("reformat_add_line: Chunk CK_LINE set to %d\n",(cell->nlines + 1) * sizeof(reformat_line)); #endif e = memory_set_chunk_size(b, cell, !cell->table ? CK_LINE : CK_LINV, /* Variable granularity allocation for table cells */ (cell->nlines + 1) * sizeof(reformat_line)); if (e) return e; /* Increment the line counter in the browser_data structure */ cell->ldata[cell->nlines].x = cell->ldata[cell->nlines].y = 0; cell->nlines++; // memset( - something! - ,0,sizeof(reformat_line)); #ifdef TRACE if (tl & (1u<<8)) Printf("reformat_add_line: Successful with cell->nlines = %d\n",cell->nlines); #endif return NULL; } /*************************************************/ /* reformat_add_line_chunk() */ /* */ /* Adds a line chunk to the array of line chunks */ /* associated with a particular line structure. */ /* This is complicated slightly by the variable */ /* length of the arrays of line chunks. The new */ /* chunk's contents are NOT initialised. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the page; */ /* */ /* Pointer to the reformat_cell */ /* structure to add the chunks to. */ /*************************************************/ static _kernel_oserror * reformat_add_line_chunk(browser_data * b, reformat_cell * cell) { int size, cline = cell->nlines - 1; _kernel_oserror * e; #ifdef TRACE if (tl & (1u<<8)) Printf("reformat_add_line_chunk: Called\n"); #endif /* The function can only ever add a chunk to the end of the list. */ /* So the chunk must belong to the last line in the list, and */ /* if it is the first chunk for the line structure that line */ /* structure's pointer to the chunks will be filled in. The chunk */ /* counter for that line is incremented. */ #ifdef TRACE /* If there are no lines, we can't proceed - something has gone wrong */ if (!cell->nlines) { erb.errnum = 0; strcpy(erb.errmess, "Serious internal error - There are no line structures defined in reformat_add_line_chunk; must exit immediately."); show_error(&erb); } #endif /* If there is no chunk data already, this is the first */ /* chunk allocation so the block size we want to move to */ /* is easy to work out. */ if (!cell->cdata) size = sizeof(reformat_line_chunk); else { /* We want to find the current size of data and put it into size. To do this, */ /* get the last line's offset to the chunks and add the number of chunks it */ /* claims to have multiplied by the size of one chunk. The complication is */ /* that the last line(s) may not have any chunks, so we need to go back to */ /* a line that does, if that is the case. */ while (!cell->ldata[cline].n && cline >= 0) cline --; #ifdef TRACE if (cline < 0) { erb.errnum = 0; strcpy(erb.errmess, "Serious internal error - No lines have associated chunks defined in reformat_add_line_chunk; must exit immediately."); show_error(&erb); } #endif size = (cell->ldata[cline].chunks + cell->ldata[cline].n + 1) * sizeof(reformat_line_chunk); } // if (cell->cdata) Printf("!! Consistency check - Current block size for chunks = %p\n",(void *) flex_size((flex_ptr) &cell->cdata)); // else Printf("!! Consistency check - Current block size for chunks = none allocated\n"); // Printf("!! New block size to be allocated = %p\n",(void *) size); /* Now allocate the memory */ #ifdef TRACE if (tl & (1u<<12)) Printf("reformat_add_line_chunk: Chunk CK_LDAT set to %d\n",size); #endif e = memory_set_chunk_size(b, cell, !cell->table ? CK_LDAT : CK_LDAV, /* Variable granularity allocation for table cells */ size); if (e) return e; /* Update the chunk counter on the last line and the base chunk */ /* array number, if it wasn't already set (when the line is */ /* created, the chunks field is filled with -1. When the first */ /* chunk is added for the line the chunks field is filled in, */ /* and it is left alone subsequently - which is exactly what we */ /* want, as the chunks field holds the base array index of the */ /* chunks for the line, not the last chunk array index. */ cline = cell->nlines - 1; cell->ldata[cline].n++; if (cell->ldata[cline].chunks < 0) cell->ldata[cline].chunks = (int) size / sizeof(reformat_line_chunk) - 1; /* Success */ #ifdef TRACE if (tl & (1u<<8)) Printf("reformat_add_line_chunk: Successful\n"); #endif return NULL; } /*************************************************/ /* reformat_reformatter() */ /* */ /* The actual working end of the reformat */ /* routines - takes the list of HStream structs */ /* (or tokens) and turns them into reformat_line */ /* structures for the redraw routines etc. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* containing all the info on the */ /* fetch for which reformatting is */ /* to take place. */ /*************************************************/ void reformat_reformatter(browser_data * b) { reformat_reformatter_r(0, b, b->cell, b->stream); } /*************************************************/ /* reformat_reformatter_r() */ /* */ /* Recursive back-end to reformat_reformatter. */ /* */ /* Parameters: Flags (see Reformat.h); */ /* */ /* Pointer to a browser_data struct */ /* relevant to the reformat session; */ /* */ /* Pointer to the reformat_cell */ /* struct that is to be acted upon; */ /* */ /* Pointer to the first token in the */ /* list of HStreams to use if there */ /* is no evidence of a previous */ /* incomplete reformat session in */ /* the browser_data structure. */ /* */ /* Returns: Width of the widest line that was */ /* generated, in millipoints. */ /*************************************************/ static int reformat_reformatter_r(unsigned int flags, browser_data * b, reformat_cell * d, HStream * streambase) { int lnCurr = -1; int cnCurr = -1; int cnLast = 0; _kernel_oserror * e = NULL; HStream * tpCurr = NULL, * tpLast = NULL; HStream * tnCurr = NULL, * tnLast = NULL; int bottom = 0, extent = 0; int newlines = 0, newline = 0; int newchunks = 0, newchunk = 0; int done = 0; int linewidth = 0; int widest = 0; int offset = 0; reformat_width_data wd; int displaywidth, left_margin, right_margin; int toplevel, doall, fromstart, noalloc; int token_has_text; /* Read various flags */ toplevel = !(flags & Reformatter_KeepGoingToEnd); /* (This may get more complex, hence 'doall' is not just '!toplevel') */ doall = (flags & Reformatter_KeepGoingToEnd); fromstart = (flags & Reformatter_FromStreamStart); if (doall) noalloc = (flags & Reformatter_Virtual); else noalloc = 0; /* If there is more than one line, find the y coordinate */ /* of the last one and put it into 'bottom' */ if (d->nlines > 2) bottom = d->ldata[d->nlines - 2].y; else if (d->nlines == 1) bottom = d->ldata[0].y; /* This y coordinate is for the last line, so it also gives the vertical extent */ extent = bottom; /* Work out the current width to format to, in millipoints. */ /* The width is the size of the visible area, but not less */ /* than min_width. */ if (flags & Reformatter_FindingWidest) displaywidth = Reformat_AsWideAsPossible_MP; else if (flags & Reformatter_FindingSmallest) displaywidth = 1600; else convert_to_points(redraw_display_width(b, d), &displaywidth); if (d->nlines && !noalloc) { /* If there are some lines, set up the local variables */ /* according to the last line's contents. */ int i; /* Set lnCurr to the last line, and set cnLast to the first chunk */ /* number on that line. Note that if the line has no chunks, the */ /* routine steps back one. The function will exit if it turns out */ /* that the first line has no chunks (after all, there's nothing */ /* to format if this is the case!) */ lnCurr = d->nlines - 1; while (!d->ldata[lnCurr].n && lnCurr >= 0) lnCurr --; if (lnCurr < 0) return 0; cnLast = d->ldata[lnCurr].chunks; /* Get the address of the HStream associated with the */ /* first chunk of the last line into tpLast. */ tpLast = d->cdata[cnLast].t; // /* Since the current tag is be at the start of this line, */ // /* strip out any preceeding spaces in it (the function */ // /* deals with the special case of preformatted text, etc.). */ // // reformat_strip_prespace(b, cnCurr, tpCurr); /* Set 'linewidth' to the indentation requried at this line, */ /* and add the widths of the line chunks to this. */ if (tpLast->style & HR) linewidth = 0; else linewidth = redraw_margin(b, d, tpLast); for ( i = 0; i < d->ldata[lnCurr].n; i++, cnLast++ ) linewidth += d->cdata[cnLast].w; if (linewidth > widest) widest = linewidth; /* If the current line has at least 1 chunk... */ if (d->ldata[lnCurr].n) { /* Set cnLast to now hold the number of the last chunk for the */ /* last line, and tpCurr and tpLast to hold the address of the */ /* token associated with the chunk. */ cnLast = d->ldata[lnCurr].n + d->ldata[lnCurr].chunks - 1; tnCurr = tnLast = tpCurr = tpLast = d->cdata[cnLast].t; /* Set 'offset' to the offset into the token associated with the last */ /* chunk in the last line, plus the amount of data to take from that */ /* token - i.e. it points to the first unused byte in that token, */ /* as far as the last chunk of the last line is concerned. */ offset = d->cdata[cnLast].o + d->cdata[cnLast].l; /* If the number of bytes into the token is <=0, then find out the */ /* total size of data normally associated with the token (e.g. the */ /* string length of any text it points to) and put this in offset. */ if (d->cdata[cnLast].l <= 0) { /* In this case associated data will *almost* always be zero length, */ /* in terms of text, but there are some rare circumstances when it */ /* won't be. Hence the reformat_datasize call. */ offset = reformat_datasize(tpLast); } /* If the tag represents text, points to a text string, and that text */ /* string terminates in a new line character, then need to start on a */ /* new line. */ if ( offset > 0 && reformat_istext(tpCurr) && tpCurr->text && tpCurr->text[offset - 1] == '\n' ) newline = 1; } else newchunk = 1; /* Otherwise, flag that a chunk needs adding */ } /* There are no line structures currently present; we aren't, then, */ /* going to add to an existing line and need to flag that a new */ /* line must be created before chunks can be added. */ else newline = 1; /* Loop round the reformatter whilst there is no error, the reformat */ /* hasn't completed (we are still reformatting and the 'done' flag */ /* isn't set), and the number of new lines dealt with is less than */ /* 10 (don't want to do too much in one go, or the Desktop will */ /* feel very jerky) or we haven't flagged a new line is needed - the */ /* reformat will only keep going for a certain number of lines, but */ /* won't stop mid-line. */ while ( !e && !done && ( doall || newlines < 10 || !newline ) && reformat_formatting(b) ) { /* tpCurr points the the token we're currently dealing with. If */ /* this is NULL, or the offset into that token seems to point */ /* past its associated data, we've finished with the token. So */ /* move onto another, or if there are no more, set 'done' to 1. */ if (tpCurr) token_has_text = tpCurr->text ? reformat_istext(tpCurr) : 0; else token_has_text = 0; // Used to be 'if (!tpCurr || offset >= reformat_datasize(tpCurr))' // Really should check that the below never differs from it. if (!token_has_text || offset < 0 || !tpCurr->text[offset]) { /* Record the last token that was dealt with in the browser_data structure */ if (!fromstart) b->last_token = tnLast; /* Advance to the next token after that last one */ if (tpLast) tnCurr = tnLast->next; else tnCurr = streambase; /* Proceed if we haven't just run out of tokens */ if (tnCurr && (tnCurr->flags & HFlags_DealtWithToken)) { tpCurr = tnCurr; /* If this token isn't of any use to the reformatter (it */ /* might correspond to header information rather than */ /* body text, say), keep getting new tokens until they */ /* are useful or we run out. If we run out, set 'done' */ /* again to show that the tokens have all been dealt */ /* with. */ while ( tpCurr && (tpCurr->flags & HFlags_DealtWithToken) && reformat_useless_token(tpCurr)) { if (!fromstart) b->last_token = tpCurr; /* Update the record of the last token dealt with */ tnCurr = tpCurr = tpCurr->next; /* Get the pointer to the new token */ } /* If we've ended up off the end of the token list (tpCurr is */ /* NULL) or on a token that the fetcher hasn't dealt with yet */ /* (the HFlags_DealtWithToken bit in the flags word is unset) */ /* then signal that the reformatter should exit by setting */ /* 'done' to 1. */ if (!tpCurr) done = 1; else { if (!(tpCurr->flags & HFlags_DealtWithToken)) { b->last_token = tpCurr->prev; tpCurr = NULL; tnCurr = NULL; done = 1; } } /* Offset is set to 0 to show this is a new token, and we haven't */ /* dealt with any of the data in it yet. */ offset = 0; } /* The current token number was greater than the number of tokens - */ /* we've run out of tokens, so flag that this in 'done'. */ else done = 1; } /* Continue with the reformat if it hasn't been flagged as finished through 'done'. */ if (!done) { /* The left hand margin - zero for horizontal rules, which can span the whole */ /* display (their actual visible extent is determined by the redraw routine); */ /* else equal to the result of redraw_margin in Redraw.c (converted to points). */ if (tpCurr->style & HR) left_margin = 0; else left_margin = redraw_margin(b, d, tpCurr); /* The right hand margin - zero for horizontal rules, which can span the whole */ /* display (their actual visible extent is determined by the redraw routine); */ /* same as the left margin for block quotations; else the right margin quantity. */ if (!(tpCurr->style & HR)) { if (tpCurr->style & BLOCKQUOTE) right_margin = left_margin; else right_margin = redraw_right_margin(b, d); } else right_margin = 0; /* If the text is not preformatted and the line width has gone over */ /* over the available display width, flag that a new line is needed */ if (!newline && !(tpCurr->style & PRE) && linewidth > displaywidth - right_margin) newline = 1; /* It generally looks better if there's a line break for tables, */ /* though this is strictly not necessary. */ if (!newline && (tpCurr->tag == TABLE && ISBODY(tpCurr))) newline = 1; /* If the difference between the current and last tags say we should */ /* put in a line break, flag this in newline. */ if (!newline && reformat_newline(tpCurr,tpLast,offset)) newline = 1; if (newline) { int y = 0; newline = 0; newchunk = 1; newlines ++; if (!noalloc) { /* Set y to hold the y coordinate the line is to be placed */ /* at. This will either be determined by the previous */ /* line's y coordinate or if there are no lines, the */ /* toolbars and aesthetic lead-in considerations. The line */ /* at this stage is of zero height; as it's height grows, */ /* the y value worked out here has the height subtracted */ /* from it to get the actual line position. */ if (lnCurr >= 0) y = d->ldata[lnCurr].y; /* (The 4 is to allow for the window frame of the toolbars) */ else y = toplevel ? reformat_y_offset(b) : 0; /* If there aren't any new chunks, the bottom line hasn't */ /* changed so set bottom to y; this stops the unchanged */ /* line being redrawn. */ if (!newchunks) bottom = y; /* Add the new line structure */ e = reformat_add_line(b, d); } else e = NULL; /* If the addition was successful, fill in various details */ if (!e) { tpCurr = tnCurr; tpLast = tnLast; if (!noalloc) { /* Advance the current line number in lnCurr to this new line */ lnCurr = d->nlines - 1; // /* Set the new line to have a default minimum height of 3 OS units, */ // /* and its bottom y coordinate must therefore be the bottom of the */ // /* page as worked out so far (in 'y') minus this height. */ // // d->ldata[lnCurr].h = 3; // d->ldata[lnCurr].y = y - 3; // // /* The 'b' field holds the y offset of the font baseline from the */ // /* bottom of the line - this is filled in somewhat arbitrarily. */ // // d->ldata[lnCurr].b = 1; d->ldata[lnCurr].h = 0; d->ldata[lnCurr].y = y; d->ldata[lnCurr].b = 0; /* The line has no chunks associated with it yet. */ d->ldata[lnCurr].n = 0; d->ldata[lnCurr].chunks = -1; } /* In light of this new line, set linewidth back to just the left margin, */ /* as there are no previous chunks to consider in working out chunk widths */ /* in the add chunk code below. */ linewidth = left_margin; if (linewidth > widest) widest = linewidth; } /* End of newline check; if newline != 0, a new line is added to the line structure array. */ } /* If there's no error, continue with the reformat procedure. */ if (!e) { /* Fill in the reformat_width_data structure */ wd.b = b; wd.tp = tpCurr; wd.data = reformat_istext(tpCurr) ? tpCurr->text : NULL; wd.offset = offset; /* The maximum width is 'very large' for preformatted text (effectively limitless). */ /* For other tokens, the left hand edge is at an indent equal to the left hand */ /* margin plus the summed widths of preceeding chunks (this is kept in 'linewidth'), */ /* so the width is the available display width minus this left hand value. */ wd.maxwid = ( ( (tpCurr->style & PRE) && reformat_istext(tpCurr) ) ) ? Reformat_AsWideAsPossible_MP : displaywidth - linewidth; wd.maxwid -= right_margin; /* Set the (returned) bytes and width fields to zero initially */ wd.bytes = wd.width = 0; /* Find out the width */ e = reformat_token_width(&wd, flags); /* Adjust the line height for tables based on */ /* the data from the above call */ if (tpCurr->tag == TABLE && ISBODY(tpCurr)) { if (!noalloc) { convert_to_os(wd.bytes, &d->ldata[lnCurr].h); d->ldata[lnCurr].y -= d->ldata[lnCurr].h; } wd.bytes = 0; } /* If the chunk width'd and defined by the above call ends in a newline character, */ /* flag a line break is needed through newline = 1. */ else if (reformat_istext(tpCurr) && wd.bytes && wd.data[wd.offset + wd.bytes - 1] == '\n') newline = 1; } /* Proceed if there is no error, to add a chunk. If the current line has no chunks, */ /* then one will always be added. If there are already chunks present, another is */ /* added only if it will fit in the display (wd.width is less than wd.maxwid), or */ /* if the text is preformatted (in which case you keep adding chunks until there's */ /* a line break in the source). */ if ( !e && ( newchunk || /* Will be set if current line has no chunks yet */ wd.width <= wd.maxwid || (tpCurr->style & PRE) ) ) { newchunk = 0; newchunks ++; if (!noalloc) e = reformat_add_line_chunk(b, d); else e = NULL; /* Proceed if the above didn't return an error */ if (!e) { tpCurr = tnCurr; tpLast = tnLast; if (!noalloc) { /* Set cnCurr to the array index of the last (i.e new) chunk; */ /* we know that if a new chunk has been added the last line */ /* must have at least the one chunk now, so no need for all */ /* the checking for various cases of lines not having chunks */ /* that has gone on elsewhere. */ cnCurr = d->ldata[d->nlines - 1].chunks + d->ldata[d->nlines - 1].n - 1; /* Fill in the new line chunk */ d->cdata[cnCurr].t = tpCurr; d->cdata[cnCurr].w = wd.width; d->cdata[cnCurr].o = offset; d->cdata[cnCurr].l = wd.bytes; /* If this holds and image, need to invalidate the x and y position */ /* information that the redraw routines set up, as it may have */ /* moved. The new position will be set when the reformatted */ /* region is next redrawn. */ if ( (tpCurr->style & IMG) || ( tpCurr->tagno == TAG_INPUT && HtmlINPUTtype(tpCurr) == inputtype_IMAGE ) ) image_set_token_image_position(b, tpCurr, -1, -1); /* If the image has a known width and height, the reformatter has */ /* dealt with it - so the image library can mark it as redrawable */ /* now (otherwise, for delayed reformats, some images which were */ /* cross referenced and did not have reformat sessions explicitly */ /* started for them, may get stuck in a 'non-redrawable' state). */ image_token_check_redrawable(b, tpCurr); #ifdef TRACE if ((tl & (1u<<20)) && tpCurr->tag == TABLE && ISBODY(tpCurr)) Printf("reformat_reformatter_r: Added a table\n"); #endif // if (d->ldata[d->nlines - 1].n == 1) // { // /* Since the current chunk will be at the start of a new line, */ // /* strip out any preceeding spaces in it (the function deals */ // /* with the special case of preformatted text, etc.). */ // // reformat_strip_prespace(b, cnCurr, tpCurr); // } // if (d->ldata[d->nlines - 1].n == 1) // { // if (tpCurr->text && !(tpCurr->style & PRE)) // { // while (*(tpCurr->text + d->cdata[cnCurr].o) == ' ' && d->cdata[cnCurr].l) d->cdata[cnCurr].o++, d->cdata[cnCurr].l--, offset++; // } // } /* Set the Visited flag if this token is a link and is in the global History */ reformat_check_visited(b, tpCurr); /* Ensure the line's height details are updated in light of the new chunk */ e = reformat_check_height(toplevel, b, d, lnCurr, tpCurr, tpLast, offset); } else e = NULL; if (!e) { int w; /* Advance through the token data */ if (wd.bytes <= 0) offset = -1; /* Flag we need a new token */ else { int has_text; offset += wd.bytes; /* Need a new line if we've not run off the end of the text - */ /* there must have been a split point within it. */ if (tpCurr) has_text = tpCurr->text ? reformat_istext(tpCurr) : 0; else has_text = 0; if (has_text && tpCurr->text[offset]) newline = 1; } /* Make the last poken pointer is now updated */ tnLast = tnCurr; tpLast = tpCurr; /* Add the chunk's width to the line width total */ linewidth += wd.width; if (linewidth > widest) widest = linewidth; /* Convert the width to OS units and compare to the minimum width for */ /* the browser so far. If the width as gone up, i.e. there is an */ /* enforced minimum for whatever reason (preformatted text, say) then */ /* update the minimum width field appropriately. */ convert_to_os(linewidth, &w); if (linewidth >= displaywidth - right_margin) { newline = 1; if (!d->table && w > b->display_extent) b->display_extent = w; } } } } else newline = 1; } } /* If there has been an error, stop the reformat and report it */ if (e) { reformat_stop(b); show_error_ret(e); /* This call returns to this point rather than jumping to the poll loop */ } else { browser_data * ancestor = utils_ancestor(b); /* For keyboard control, try to find something to select. */ /* Browser windows that have children can't have visible */ /* tokens, so don't bother for them, and for anything */ /* with a selected item, check that it has't acquired */ /* children over the reformat - if it has, move the */ /* selection out of there. */ if (choices.keyboard_ctrl) { /* Clear the selection if the owner has acquired children */ if (ancestor->selected) { browser_data * owner = ancestor->selected_owner; if (owner && owner->nchildren) browser_clear_selection(owner, 0); } /* If there is no selection (whether this is due to the */ /* above code or not), and the current browser has no */ /* children, try to select something in it. */ if (!ancestor->selected && !b->nchildren) { ancestor->selected = browser_find_first_selectable(b, NULL, 0); if (ancestor->selected) ancestor->selected_owner = b; } } } /* Make sure the window is the right size and ensure */ /* that the altered regions are redrawn, unless */ /* we are printing (so the reformat doesn't actually */ /* correspond to any real window). */ if (toplevel && !printing) { reformat_check_extent(b); browser_update_bottom(b, bottom + 4); } /* Return the width of the widest line generated in this session, in millipoints */ return widest; } /*************************************************/ /* reformat_format_cell() */ /* */ /* Reformats a specific table cell to a given */ /* width. */ /* */ /* Parameters: 1 for a top level call, 0 if */ /* being called as part of a nested */ /* table parse; */ /* */ /* Pointer to a browser_data struct */ /* relevant to the table; */ /* */ /* Pointer to the first HStream */ /* structure in the stream that will */ /* be formatted into the cell; */ /* */ /* Pointer to the table_stream */ /* struct representing the table; */ /* */ /* Pointer to the table's array of */ /* reformat_cell structures; */ /* */ /* Allowed cell (column) width, in */ /* millipoints; */ /* */ /* Row number of the cell; */ /* */ /* Column number of the cell. */ /* */ /* Returns: Actual final cell width in */ /* millipoints. */ /*************************************************/ int reformat_format_cell(int toplevel, browser_data * b, HStream * streambase, table_stream * table, reformat_cell * cellarray, int ColWidth, int Row, int Column) { int dheight; int cellindex = Row * table->ColSpan + Column; reformat_cell * c; /* Can't do anything if the cell index is out of range */ if (cellindex >= table->RowSpan * table->ColSpan) return 1600; else c = &cellarray[cellindex]; #ifdef TRACE if (tl & (1u<<20)) { Printf("tables_width_cell: %p %d %d %d %d\n",streambase,ColWidth,table->ColSpan,Row,Column); Printf("tables_width_cell in: %d\n", ColWidth / 400); } #endif /* Format the cell to the specified width. If 'toplevel' is 0, this is */ /* being called as part of a format for a parent table, so don't */ /* generate lines (flag Virtual in the reformatter). Otherwise, allow */ /* line generation (don't flag Virtual). */ c->width = c->cellwidth = ColWidth; reformat_reformatter_r(Reformatter_KeepGoingToEnd | Reformatter_FromStreamStart | (toplevel ? Reformatter_Virtual : 0), b, c, streambase); convert_to_points(reformat_return_extent(b, c), &dheight); c->height = dheight; #ifdef TRACE if (tl & (1u<<20)) { Printf("width found is %d\n", c->width); Printf("height found is %d\n",c->height); } #endif return c->width; } /*************************************************/ /* reformat_find_cell_limits() */ /* */ /* Works out the narrowest and widest widths a */ /* given table cell could be, in millipoints. */ /* */ /* Parameters: 1 for a top level call, 0 if */ /* being called as part of a nested */ /* table parse; */ /* */ /* Pointer to a browser_data struct */ /* relevant to the table; */ /* */ /* Pointer to the first HStream */ /* structure in the stream that the */ /* cell is to contain; */ /* */ /* Pointer to the table_stream */ /* struct representing the table; */ /* */ /* Pointer to the table's array of */ /* reformat_cell structures; */ /* */ /* Row number of the cell; */ /* */ /* Column number of the cell; */ /* */ /* Pointer to an int, in which the */ /* minimum width is returned; */ /* */ /* Pointer to an int, in which the */ /* maximum width is returned; */ /*************************************************/ void reformat_find_cell_limits(int toplevel, browser_data * b, HStream * streambase, table_stream * table, reformat_cell * cellarray, int Row, int Column, int * retmin, int * retmax) { int maxwidth = 0; int minwidth = 0; int cellindex = Row * table->ColSpan + Column; reformat_cell * c; /* Can't find the limits of something outside the cell range */ /* of the table... */ if (cellindex >= table->RowSpan * table->ColSpan) { if (retmin) *retmin = 1600; if (retmax) *retmax = 1600; return; } else c = &cellarray[cellindex]; /* Find the maximum width used by the cell; first, reformat */ /* to a 'large width' (effectively, no line breaks). */ maxwidth = reformat_reformatter_r(Reformatter_KeepGoingToEnd | Reformatter_FindingWidest | Reformatter_FromStreamStart | Reformatter_Virtual, b, c, streambase); #ifdef TRACE if (c->nlines || c->ldata || c->cdata) { erb.errnum = Utils_Error_Custom_Normal; strcpy(erb.errmess,"Line or chunk data allocated inside reformat_find_cell_limits for maxwidth check"); show_error_ret(&erb); } #endif /* Find the minimum width */ minwidth = reformat_reformatter_r(Reformatter_KeepGoingToEnd | Reformatter_FindingSmallest | Reformatter_FromStreamStart | Reformatter_Virtual, b, c, streambase); #ifdef TRACE if (c->nlines || c->ldata || c->cdata) { erb.errnum = Utils_Error_Custom_Normal; strcpy(erb.errmess,"Line or chunk data allocated inside reformat_find_cell_limits for minwidth check"); show_error_ret(&erb); } #endif // Should do the CELLPADDING stuff here, but where is the actual table // definition HStream? The TableStream passed in is just the head of // the data stream, not the definition. // This would be an 'else' to having no cellpadding info. // { // /* Default 8 OS unit padding for aesthetics */ // // minwidth += 3200; // maxwidth += 3200; // } if (retmin) *retmin = minwidth; if (retmax) *retmax = maxwidth; } /*************************************************/ /* reformat_change_text() */ /* */ /* Used to alter text in a tag, to provide (for */ /* example) smart quotes handling. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* with information on the fetch */ /* Pointer to a token to alter. */ /* */ /* Assumes: The browser_data struct may not */ /* be NULL, but the token pointer */ /* can be and the token does not */ /* have to contain text. */ /*************************************************/ void reformat_change_text(browser_data * b, HStream * tp) { // Note that this, then, does nothing right now - fix this function up some time! return; if (tp && tp->text && !(tp->style & TT) && (reformat_istext(tp))) { char * curr = tp->text; while (*curr) { if (*curr == '`') *curr = 148; /* Always make this quote an opening quote */ if (*curr == '\'') /* Dumb single quote */ { if (b->last_char == ' ' || b->last_char == '(' || b->last_char == 148 /* Opening double quote */ || b->last_char == '\"') *curr = 144; /* Opening single quote */ else *curr = 145; /* Closing single quote */ } else if (*curr == '\"') /* Dumb double quote */ { if (b->last_char == ' ' || b->last_char == '(' || b->last_char == 144 /* Opening single quote */ || b->last_char == '\'' || b->last_char == '`') *curr = 148; /* Opening double quote */ else *curr = 149; /* Closing double quote */ } else if (*curr == '-' && b->last_char == ' ') *curr = 151; /* 'en' dash */ // else if (*curr == '-' && b->last_char == 151) memmove - something! b->last_char = *curr; curr ++; } } /* If we have ALT text for an image, strip off any preceeding */ /* spaces or [s, and any trailing spaces or ]s. */ else if ( tp && tp->text && ( (tp->style & IMG) || ( tp->tagno == TAG_INPUT && HtmlINPUTtype(tp) == inputtype_IMAGE ) ) ) { char * start, * end; char last; int len; len = strlen(tp->text); /* Get rid of preceeding characters */ start = tp->text; end = tp->text + len - 1; while (*start == ' ' || *start == '[') start ++; /* If there's anything left... */ if (*start) { /* Get rid of trailing characters */ while (*end == ' ' || *end == ']') { *end = '\0'; end --; } } /* If there's still something left, move the */ /* string contents down so tp->text points */ /* past the stripped preceeding chracters. */ if (start <= end) memmove(tp->text, start, strlen(start) + 1); /* 'strlen + 1' to get the string terminator */ else { /* If there was nothing left, did we originally have */ /* enough to put '[]' to mark that there's no text left? */ if (len > 1) strcpy(tp->text,"[]"); } /* Now do smart quotes substitution. Need to do this */ /* separately from the main text routines as the */ /* last_char variable must not be changed by ALT text */ /* - it stands alone for each image. */ last = ' '; start = tp->text; while (*start) { if (*start == '\'' || *start == '`') /* Dumb single quote */ { if (last == ' ' || last == '(' || last == 148 /* Opening double quote */ || last == '\"') *start = 144; /* Opening single quote */ else *start = 145; /* Closing single quote */ } else if (*start == '\"') /* Dumb double quote */ { if (last == ' ' || last == '(' || last == 144 /* Opening single quote */ || last == '\'' || last == '`') *start = 148; /* Opening double quote */ else *start = 149; /* Closing double quote */ } else if (*start == '-' && last == ' ') *start = 151; /* 'en' dash */ last = *start; start ++; } } }