/* 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 : TokenUtils.c */ /* */ /* Purpose: Utility functions for finding out */ /* information about tokens. */ /* */ /* Author : A.D.Hodgkinson */ /* */ /* History: 09-Apr-97: Created. */ /***************************************************/ #include #include #include #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 "swis.h" #include "kernel.h" #include "toolbox.h" #include "event.h" #include "svcprint.h" #include "Global.h" #include "MiscDefs.h" #include "Utils.h" #include "Redraw.h" #include "TokenUtils.h" /* Locals */ static tokenutils_npaths = 0; /* Static function prototypes */ static int tokenutils_line_range_r(int toplevel, browser_data * b, reformat_cell * d, HStream * token, int * fline, int * fchunk, int * lline, int * lchunk, token_path ** path); /*************************************************/ /* tokenutils_anchor_range() */ /* */ /* Will return the first (inclusive) and last */ /* (inclusive) tokens that consecutively point */ /* to the same thing as anchors. This is useful */ /* as some links may be made of several tokens, */ /* as style changes to mark the result of a web */ /* search (for example) or whatever may be used */ /* in what is meant to be the same link. If this */ /* link is to be highlighted or selected, need */ /* to know which tokens are involved. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the tokens; */ /* */ /* Pointer to one of the tokens in */ /* the collection for this anchor; */ /* */ /* Pointer to an HStream pointer, */ /* into which the address of the */ /* first token involved in the link */ /* is placed; */ /* */ /* Pointer to an HStream pointer, */ /* into which the address of the */ /* last token involved in the link */ /* is placed. */ /* */ /* Assumes: Either of the last two pointers */ /* may be NULL. The token pointer */ /* given at the start should point */ /* to a link. If not, the returned */ /* addresses will be undefined... */ /*************************************************/ void tokenutils_anchor_range(browser_data * b, HStream * token, HStream ** first, HStream ** last) { HStream * top = NULL; HStream * end = NULL; HStream * store = NULL; if (first) *first = NULL; if (last) *last = NULL; if (!b || !token) return; /* Can only proceed if the given token represents a link */ if (token && (token->style & A) && token->anchor) { store = top = token; /* Move up the token list for as long as the token given matches */ /* the details of the token being looked at. */ while ( top && (top->style & A) && top->anchor && !strcmp(top->anchor, token->anchor) ) store = top, top = top->prev; /* Make sure we haven't overshot the first item */ if (!top) top = store; if ( top && !( (top->style & A) && top->anchor && !strcmp(top->anchor, token->anchor) ) ) top = top->next; store = end = token; /* Similary, move down the token list to find the first token after */ /* all those involved in the link. */ while ( end && (end->flags & HFlags_DealtWithToken) && (end->style & A) && end->anchor && !strcmp(end->anchor, token->anchor) ) store = end, end = end->next; /* Ensure we haven't overshot */ if (!end) end = store; if ( end && !( (end->flags & HFlags_DealtWithToken) && (end->style & A) && end->anchor && !strcmp(end->anchor, token->anchor) ) ) end = end->prev; } else top = end = token; /* Return the found values */ if (first) *first = top; if (last) *last = end; return; } /*************************************************/ /* tokenutils_line_range() */ /* */ /* Returns the first chunk and line number to */ /* use a given token, and the last line and */ /* chunk number to use that token. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the token; */ /* */ /* Pointer to the token; */ /* */ /* Pointer to an int, into which the */ /* first line number is placed; */ /* */ /* Pointer to an int, into which the */ /* first chunk number is placed; */ /* */ /* Pointer to an int, into which the */ /* last line number is placed; */ /* */ /* Pointer to an int, into which the */ /* last chunk number is placed; */ /* */ /* Pointer to a token_path pointer, */ /* which will be filled in with the */ /* address of a malloced array of */ /* reformat_cell structures for */ /* tokens within tables, or NULL if */ /* this shouldn't be built. */ /* */ /* Returns: See parameters list, and note */ /* that '-1' will be returned in all */ /* four fields should be token not */ /* be found in a line to start with; */ /* */ /* The number of entries in the */ /* associated token_path structure */ /* for table-based tokens. */ /* */ /* Assumes: Any of the pointers may be NULL. */ /*************************************************/ int tokenutils_line_range(browser_data * b, HStream * token, int * fline, int * fchunk, int * lline, int * lchunk, token_path ** path) { tokenutils_npaths = 0; if (path) *path = NULL; return tokenutils_line_range_r(1, b, b->cell, token, fline, fchunk, lline, lchunk, path); } /*************************************************/ /* tokenutils_line_range_r() */ /* */ /* Recursive back-end to tokenutils_line_range. */ /* */ /* Parameters: 1 for a top level call, else 0 if */ /* being called recursively; */ /* */ /* Pointer to a browser_data struct */ /* relevant to the token; */ /* */ /* Pointer to a reformat_cell struct */ /* containing the token or a table */ /* which holds the token; */ /* */ /* Pointer to the token; */ /* */ /* Pointer to an int, into which the */ /* first line number is placed; */ /* */ /* Pointer to an int, into which the */ /* first chunk number is placed; */ /* */ /* Pointer to an int, into which the */ /* last line number is placed; */ /* */ /* Pointer to an int, into which the */ /* last chunk number is placed; */ /* */ /* Pointer to a token_path pointer, */ /* which will be filled in with the */ /* address of a malloced array of */ /* token_path structures for tokens */ /* within tables, or NULL if this */ /* shouldn't be built. */ /* */ /* Returns: As for tokenutils_line_range. */ /* */ /* Assumes: As for tokenutils_line_range. */ /*************************************************/ static int tokenutils_line_range_r(int toplevel, browser_data * b, reformat_cell * d, HStream * token, int * fline, int * fchunk, int * lline, int * lchunk, token_path ** path) { int fl = -1, fc = -1, ll = -1, lc = -1; /* First Line, First Chunk, Last Line, Last Chunk */ /* Start with all values at -1; saves having to fill them */ /* in at any given failure point. */ if (fline) *fline = fl; if (fchunk) *fchunk = fc; if (lline) *lline = ll; if (lchunk) *lchunk = lc; /* Can only try to find the token if the browser_data */ /* and token pointers were not NULL. */ if (b && d && token && d->nlines) { int cl, cc = -1, mc; /* Current Line, Current Chunk, Maximum Chunk */ int found = 0; /* Must find the first line / chunk even if not asked for it */ cl = 0; /* Loop through all lines */ while (cl < d->nlines && !found) { cc = d->ldata[cl].chunks; mc = cc + d->ldata[cl].n; /* Go through this line's chunks looking for the token */ while (cc < mc && !found) { if (toplevel) tokenutils_npaths = 0; if (d->cdata[cc].t == token) found = 1; else { /* Must recursively scan token lists for tables */ if (d->cdata[cc].t->tagno == TAG_TABLE) { table_stream * table = (table_stream *) d->cdata[cc].t; table_row * row = NULL; table_headdata * head = NULL; reformat_cell * cellarray = table->cells; reformat_cell * cell; int found = 0; int cellindex; int cellcount = 0; int cellmax = table->ColSpan * table->RowSpan; /* Naming convention: [Pointer] Table First/Last Line/Chunk */ int tfl = -1, tfc = -1, tll = -1, tlc = -1; int * ptfl, * ptfc, * ptll, * ptlc; if (cellarray) { /* For recursive scanning of line lists, keep efficiency */ /* as high as possible by only asking for the same */ /* values as originally passed to the function (e.g. */ /* don't want to look at last lines or chunks during */ /* the recursive scan unless they are needed at the top */ /* level too). */ if (fline) ptfl = &tfl; else ptfl = NULL; if (fchunk) ptfc = &tfc; else ptfc = NULL; if (lline) ptll = &tll; else ptll = NULL; if (lchunk) ptlc = &tlc; else ptlc = NULL; /* Scan the table */ row = table->List; while (row && !found && cellcount < cellmax) { head = row->List; while (head && !found && cellcount < cellmax) { switch (head->Tag) { case TagTableData: case TagTableHead: { cellindex = table->ColSpan * head->RowOffs + head->ColOffs; if (cellindex < cellmax) { cell = &cellarray[cellindex]; /* If a cell corresponding to this headdata item can be */ /* found and it has lines to scan, call this function */ /* recursively on its contents. */ if (cell && cell->nlines) { /* Move flex anchors into the temporary browser_data struct */ tokenutils_line_range_r(0, b, cell, token, ptfl, ptfc, ptll, ptlc, path); /* Check if the token was found */ if (tfl != -1 || tfc != -1 || tll != -1 || tlc != -1) found = 1; } } } break; } if (!found) { cellcount++; head = head->Next; } } if (!found) row = row->Next; } if (found) { if (path) { token_path * temp; /* Increment the tokenutils_npaths counter and allocate memory for a */ /* new token_path structure. */ tokenutils_npaths++; /* Note 'tokenutils_npaths + 1' - an extra structure for marking the */ /* end of the array. */ if (*path) temp = realloc(*path, (tokenutils_npaths + 1) * sizeof(token_path)); else temp = malloc((tokenutils_npaths + 1) * sizeof(token_path)); /* Complain and bail out if the allocation failed */ if (!temp) { if (*path) { free(*path); *path = NULL; } show_error_ret(make_no_memory_error(1)); return 0; } else *path = temp; /* Fill in the new token_path structure */ (*path)[tokenutils_npaths - 1].line = cl; (*path)[tokenutils_npaths - 1].chunk = cc; (*path)[tokenutils_npaths - 1].head = head; /* Fill in the terminating structure */ (*path)[tokenutils_npaths].line = -1; (*path)[tokenutils_npaths].chunk = -1; (*path)[tokenutils_npaths].head = NULL; } /* Since we've found the token within a table inside this line, */ /* and the token_path is filled in, want to exit now. */ if (fline) *fline = tfl; if (fchunk) *fchunk = tfc; if (lline) *lline = tll; if (lchunk) *lchunk = tlc; return tokenutils_npaths; } /* Closure of 'if (cellarray)' */ } /* Closure of 'if (d->cdata[cc].t->tagno == TAG_TABLE)' */ } cc++; /* Closure of 'else' section to 'if (d->cdata[cc].t == token)' */ } /* Closure of 'while' loop going through all the chunks */ } /* If we haven't found a chunk yet, go to the next line */ if (!found) cl ++; } if (found) { if (cl >= d->nlines) cl = d->nlines - 1; fl = cl, fc = cc; /* If a line and chunk have been found, record them in fl and fc */ if (cl >= 0 && cc >= 0) { /* Only try to find the last line / chunk if asked for them */ if (lline || lchunk) { /* Start at the next line and if this starts with the given */ /* token, keep moving on until a line which doesn't start */ /* with the token is found. */ cl++; while (cl < d->nlines && d->cdata[d->ldata[cl].chunks].t == token) cl++; /* Coming out of the above loop either we'll be at cl = d->nlines, */ /* or cl will be the first line that doesn't start with the given */ /* token. In either case, must decrement cl by one to get back to */ /* a line which does include the token. */ cl--; if (lchunk) { /* Now search the chunks for the token, starting at the last chunk */ cc = d->ldata[cl].chunks + d->ldata[cl].n - 1; while (cc >= d->ldata[cl].chunks && d->cdata[cc].t != token) cc--; /* If cc is still in range, a token was found on this line (it */ /* should always be, but something might go wrong above and */ /* this check is worth doing for robustness). So copy the last */ /* line and chunk details to ll and lc. */ if (cc >= d->ldata[cl].chunks) ll = cl, lc = cc; } else ll = cl; } } } } /* Fill in any requested values */ if (fline) *fline = fl; if (fchunk) *fchunk = fc; if (lline) *lline = ll; if (lchunk) *lchunk = lc; return tokenutils_npaths; } /*************************************************/ /* tokenutils_find_ancestor_line() */ /* */ /* Returns the line in a given browser's main */ /* line list in which a given token lies, or -1 */ /* if the token can't be found. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the token and lines; */ /* */ /* Pointer to the HStream struct to */ /* find. */ /* */ /* Returns: Line number that the token lies */ /* in, in the main line list - so */ /* the token could well lie inside */ /* a table within that line. -1 is */ /* returned if the token is not */ /* found in the line list at all. */ /*************************************************/ int tokenutils_find_ancestor_line(browser_data * b, HStream * t) { int l; int chunk, depth, noline = 0; token_path * path = NULL; depth = tokenutils_line_range(b, t, &l, &chunk, NULL, NULL, &path); if (l < 0) noline = 1; else { /* If a line was found and depth is non-zero, the line was inside */ /* a table - want to find the parent line of the table */ if (depth) l = path[depth - 1].line; if (l < 0) noline = 1; } if (path) free(path); if (noline) return -1; return l; } /*************************************************/ /* tokenutils_find_token */ /* */ /* For a given token in a given reformat_cell, */ /* returns the line and chunk number that the */ /* token spans. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the token; */ /* */ /* Pointer to a reformat_cell struct */ /* containing the token or a table */ /* which holds the token; */ /* */ /* Pointer to the token; */ /* */ /* Pointer to an int, into which the */ /* first line number is placed; */ /* */ /* Pointer to an int, into which the */ /* first chunk number is placed; */ /* */ /* Pointer to an int, into which the */ /* last line number is placed; */ /* */ /* Pointer to an int, into which the */ /* last chunk number is placed; */ /* */ /* Returns: See parameters list, and note */ /* that '-1' will be returned in all */ /* four fields should be token not */ /* be found in a line to start with; */ /* */ /* Assumes: Any of the pointers may be NULL. */ /*************************************************/ void tokenutils_find_token(browser_data * b, reformat_cell * d, HStream * token, int * fline, int * fchunk, int * lline, int * lchunk) { int fl = -1, fc = -1, ll = -1, lc = -1; /* First Line, First Chunk, Last Line, Last Chunk */ /* Start with all values at -1; saves having to fill them */ /* in at any given failure point. */ if (fline) *fline = fl; if (fchunk) *fchunk = fc; if (lline) *lline = ll; if (lchunk) *lchunk = lc; /* Can only try to find the token if the browser_data */ /* and token pointers were not NULL. */ if (b && d && token && d->nlines) { int cl, cc = -1, mc; /* Current Line, Current Chunk, Maximum Chunk */ int found = 0; /* Must find the first line / chunk even if not asked for it */ cl = 0; /* Loop through all lines */ while (cl < d->nlines && !found) { cc = d->ldata[cl].chunks; mc = cc + d->ldata[cl].n; /* Go through this line's chunks looking for the token */ while (cc < mc && !found) { if (d->cdata[cc].t == token) found = 1; /* If not found, go to the next chunk */ else cc++; } /* If we haven't found a chunk yet, go to the next line */ if (!found) cl ++; } if (found) { if (cl >= d->nlines) cl = d->nlines - 1; fl = cl, fc = cc; /* If a line and chunk have been found, record them in fl and fc */ if (cl >= 0 && cc >= 0) { /* Only try to find the last line / chunk if asked for them */ if (lline || lchunk) { /* Start at the next line and if this starts with the given */ /* token, keep moving on until a line which doesn't start */ /* with the token is found. */ cl++; while (cl < d->nlines && d->cdata[d->ldata[cl].chunks].t == token) cl++; /* Coming out of the above loop either we'll be at cl = d->nlines, */ /* or cl will be the first line that doesn't start with the given */ /* token. In either case, must decrement cl by one to get back to */ /* a line which does include the token. */ cl--; if (lchunk) { /* Now search the chunks for the token, starting at the last chunk */ cc = d->ldata[cl].chunks + d->ldata[cl].n - 1; while (cc >= d->ldata[cl].chunks && d->cdata[cc].t != token) cc--; /* If cc is still in range, a token was found on this line (it */ /* should always be, but something might go wrong above and */ /* this check is worth doing for robustness). So copy the last */ /* line and chunk details to ll and lc. */ if (cc >= d->ldata[cl].chunks) ll = cl, lc = cc; } else ll = cl; } } } } /* Fill in any requested values */ if (fline) *fline = fl; if (fchunk) *fchunk = fc; if (lline) *lline = ll; if (lchunk) *lchunk = lc; /* Finished */ return; } /*************************************************/ /* tokenutils_token_cell_offset() */ /* */ /* When a token has been found to lie in a table */ /* (tokenutils_line_range has filled in an */ /* array of token_path structures) this call */ /* can be used to quickly find the x and y */ /* offset from the top left of the main page */ /* that the line list the token was in is */ /* positioned at (i.e. for tables, the offset of */ /* the table cell the token lies in). This can */ /* be used as an origin shift for any subsequent */ /* redraws (say). */ /* */ /* External callers trying to then find the */ /* token x and y position should remember to */ /* work this out for the correct line array, */ /* which will be indicated by the first entry */ /* in the token_path array. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the token_path array; */ /* */ /* Pointer to the token_path array; */ /* */ /* Pointer to an int, in which the */ /* x offset will be written; */ /* */ /* Pointer to an int, in which the */ /* y offset will be written. */ /* */ /* Assumes: Any pointer may be NULL. If the */ /* token_path array is NULL, then */ /* an offset of 0,0 is given. */ /*************************************************/ void tokenutils_token_offset(browser_data * b, token_path * path, int * offset_x, int * offset_y) { int actx = 0, acty = 0; reformat_cell * cell = b->cell; /* Only start working things out if there's a token_path array */ if (path && tokenutils_npaths) { int index = tokenutils_npaths - 1; int line, chunk; table_stream * table; table_headdata * head; reformat_cell * cellarray = NULL; reformat_cell * c = NULL; /* Find the line, chunk, headdata structure, table structure and */ /* cell array for the last item in the token_path array. */ line = path[index].line; chunk = path[index].chunk; head = path[index].head; table = (table_stream *) cell->cdata[chunk].t; cellarray = table->cells; /* Increment by the offset of the top left of the table. */ /* Since the last item in the array corresponds to the */ /* browser_data structure's cell array itself, here we */ /* use b->cell (stored in 'cell' above) rather than a */ /* table-derived cell. */ actx += redraw_start_x(b, cell, cell->cdata[chunk].t, line); acty += cell->ldata[line].y + cell->ldata[line].h; /* If there's a cell array, find the cell the current headdata */ /* item is represented by. */ if (cellarray) c = &cellarray[table->ColSpan * head->RowOffs + head->ColOffs]; /* If there's a cell, offset actx and acty by the top left */ /* hand offset of that cell. */ if (c) { int incx, incy; convert_pair_to_os(c->x, c->y, &incx, &incy); actx += incx, acty += incy; } /* Now do much the same for the remaining items in the token_path array */ index--; while (index >= 0 && c && c->ldata && c->cdata) { line = path[index].line; chunk = path[index].chunk; head = path[index].head; table = (table_stream *) c->cdata[chunk].t; cellarray = table->cells; actx += redraw_start_x(b, c, c->cdata[chunk].t, line); acty += c->ldata[line].y + c->ldata[line].h; if (cellarray) c = &cellarray[table->ColSpan * head->RowOffs + head->ColOffs]; if (c) { int incx, incy; convert_pair_to_os(c->x, c->y, &incx, &incy); actx += incx, acty += incy; } index--; } } /* actx and acty will either have been calculated from the token_path */ /* array or will still be at 0 if no array was given. */ if (offset_x) *offset_x = actx; if (offset_y) *offset_y = acty; return; } /*************************************************/ /* tokenutils_find_cell() */ /* */ /* Examines a token_path array and returns the */ /* address of the reformat_cell structure that */ /* corresponds to the last entry. */ /* */ /* Parameters: Pointer to the reformat_cell that */ /* the token_path array refers to in */ /* the first entry (e.g. the 'cell' */ /* field of the parent browser_data */ /* structure); */ /* */ /* Number of entries in the array; */ /* */ /* Pointer to the first entry. */ /* */ /* Returns: Pointer to the reformat_cell */ /* structure corresponding to the */ /* last entry, or NULL if none may */ /* be found. */ /*************************************************/ reformat_cell * tokenutils_find_cell(reformat_cell * cell, int depth, token_path * path) { if (depth && path && path[0].line >= 0 && path[depth - 1].line >= 0) { table_stream * table; table_headdata * head; reformat_cell * cellarray = NULL; reformat_cell * c = NULL; int index = depth - 1; int line, chunk; /* Start looking at the token_path array. The last */ /* entry refers to the main line array, so get to */ /* the appropriate reformat_cell structure through */ /* the main line list. */ line = path[index].line; chunk = path[index].chunk; head = path[index].head; table = (table_stream *) cell->cdata[chunk].t; cellarray = table->cells; if (cellarray) c = &cellarray[table->ColSpan * head->RowOffs + head->ColOffs]; index--; /* Need to look at subsequent cell line arrays for any */ /* nested tables. */ while (index >= 0 && c) { line = path[index].line; chunk = path[index].chunk; head = path[index].head; table = (table_stream *) c->cdata[chunk].t; cellarray = table->cells; if (cellarray) c = &cellarray[table->ColSpan * head->RowOffs + head->ColOffs]; index--; } return c; } return NULL; } /*************************************************/ /* tokenutils_token_cell() */ /* */ /* Returns the reformat_cell that a given token */ /* lies in. This may be NULL. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the token; */ /* */ /* Pointer to the token. */ /*************************************************/ reformat_cell * tokenutils_token_cell(browser_data * b, HStream * token) { table_headdata * head; table_row * row; table_stream * table; reformat_cell * cellarray; int cellnumber; if (!b || !token) return NULL; /* Find the table the token lies in */ head = token->parent; if (!head) return NULL; row = head->parent; if (!row) return NULL; table = row->parent; if (!table) return NULL; cellarray = table->cells; if (!cellarray) return NULL; /* Work out what number this cell is in the cell array */ cellnumber = head->ColOffs + table->ColSpan * head->RowOffs; if (cellnumber > table->ncells) return NULL; else return &cellarray[cellnumber]; } /*************************************************/ /* tokenutils_within_distance() */ /* */ /* Returns 1 if a given token is within a given */ /* distance of another, vertically. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the tokens; */ /* */ /* Pointer to the first token; */ /* */ /* Pointer to the second token; */ /* */ /* Distance that the tokens must be */ /* within. */ /* */ /* Returns: 1 if the tokens are within the */ /* given distance of each other, */ /* else 0. */ /* */ /* Assumes: If either HStream pointer is NULL */ /* the function return 1. */ /*************************************************/ int tokenutils_within_distance(browser_data * b, HStream * t1, HStream * t2, int distance) { int lfir1, llas1, lfir2, llas2; int check; reformat_cell * cell = b->cell; if (!t1 || !t2) return 1; /* Ensure distance is a positive number and find the */ /* lines that the tokens span. */ if (distance < 0) distance = -distance; tokenutils_line_range(b, t1, &lfir1, NULL, &llas1, NULL, NULL); tokenutils_line_range(b, t2, &lfir2, NULL, &llas2, NULL, NULL); /* Special case, tokens must be at zero distance effectively */ if (lfir1 == lfir2 || llas1 == llas2) return 1; /* Want to ensure that the maximum distance between the tokens */ /* is taken for the check. E.g. if t1 is above t2 (i.e. lfir1 */ /* is less than lfir2) then want to check the distance between */ /* the top line of t1 to the bottom line of t2. */ if (lfir1 < lfir2) check = cell->ldata[lfir1].y + cell->ldata[lfir1].h - cell->ldata[lfir2].y; else check = cell->ldata[lfir2].y + cell->ldata[lfir2].h - cell->ldata[lfir1].y; return (check <= distance); }