/* 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 : Browser.c */ /* */ /* Purpose: Browser window services. */ /* */ /* Author : A.D.Hodgkinson */ /* */ /* History: 15-Mar-97: Created from Windows.c. */ /***************************************************/ #include <stdlib.h> #include <string.h> #include "swis.h" #include "flex.h" #include "wimp.h" #include "wimplib.h" #include "event.h" #include "toolbox.h" #include "window.h" #include "Dialler.h" #include "NestWimp.h" #include "svcprint.h" #include "Global.h" #include "FromROSLib.h" #include "MiscDefs.h" #include "Utils.h" #include "CSIM.h" #include "Fetch.h" /* (Which itself includes URLstat.h) */ #include "Frames.h" #include "Forms.h" #include "History.h" #include "Images.h" #include "Mouse.h" #include "PrintStyle.h" #include "Redraw.h" #include "Reformat.h" #include "TokenUtils.h" #include "Toolbars.h" #include "Browser.h" /* Static function prototypes */ static HStream * browser_find_selectable_top_r (browser_data * b, reformat_cell * cell, HStream ** current, int y_origin, WimpGetWindowStateBlock * s); static HStream * browser_find_selectable_bot_r (browser_data * b, reformat_cell * cell, HStream ** current, int y_origin, WimpGetWindowStateBlock * s); static int browser_navigate_map (browser_data * b, int key); static _kernel_oserror * browser_redraw_border (browser_data * b, HStream * token); static HStream * browser_get_pointer_token_r (browser_data * b, reformat_cell * cell, WimpGetPointerInfoBlock * p, WimpGetWindowStateBlock * state, int * ox, int * oy); static int browser_top_line_r (browser_data * b, reformat_cell * cell, reformat_cell ** ret_cell, WimpGetWindowStateBlock * s, int fully_visible); static int browser_bottom_line_r (browser_data * b, reformat_cell * cell, reformat_cell ** ret_cell, WimpGetWindowStateBlock * s, int fully_visible); static _kernel_oserror * browser_update_token_r (browser_data * b, reformat_cell * cell, HStream * token, int first, int last, int chunk, int base_x, int base_y, int noback, HStream * nocontent); static _kernel_oserror * browser_set_look_r (browser_data * b, ObjectId source, int underline_links, int use_source_cols, int show_foreground, int show_background); /* Local statics */ static ObjectId pointer_is_over = 0; /* Object that the pointer is over, if any */ /*************************************************/ /* browser_scroll_page_v() */ /* */ /* Scrolls a page vertically by a given amount. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the page; */ /* */ /* A WimpOpenWindowBlock pointer, */ /* holding the window's current */ /* details (e.g. visible area) or */ /* NULL if this is not known; */ /* */ /* 1 to scroll up, else down; */ /* */ /* 1 to page up/down, else 0; */ /* */ /* 1 to move one line, else 0; */ /* */ /* An amount to scroll by, ignored */ /* unless the above two parameters */ /* are zero; */ /* */ /* Pointer to an int, in which 1 is */ /* written if the window didn't */ /* shift scroll position as it was */ /* at the limit of its work area, */ /* else 0 is written. */ /*************************************************/ _kernel_oserror * browser_scroll_page_v(browser_data * b, WimpOpenWindowBlock * o, int dir, int page, int line, int amount, int * limit) { int scrollby; WimpGetWindowStateBlock open; _kernel_oserror * e; /* Work out the WimpOpenWindowBlock if NULL was passed in */ if (!o) { open.window_handle = b->window_handle; e = wimp_get_window_state(&open); if (e) return e; o = (WimpOpenWindowBlock *) &open; } if (limit) { /* If required, see if we're at the limit of the scroll position */ BBox extent; int scrollmax; e = window_get_extent(0, b->self_id, &extent); if (e) return e; scrollmax = !dir ? extent.ymin + (o->visible_area.ymax - o->visible_area.ymin) : 0; if (scrollmax == o->yscroll) *limit = 1; else *limit = 0; } /* Work out how much to scroll by */ if (page) scrollby = o->visible_area.ymax - o->visible_area.ymin - toolbars_url_height(b) - toolbars_button_height(b) - toolbars_status_height(b) - wimpt_dy(); else if (line) scrollby = 42; else scrollby = amount; /* If greater than zero, move the page */ if (scrollby > 0) { if (!dir) scrollby = -scrollby; o->yscroll += scrollby; return wimp_open_window(o); } return NULL; } /*************************************************/ /* browser_scroll_page_h() */ /* */ /* Scrolls a page horizontally a given amount. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the page; */ /* */ /* A WimpOpenWindowBlock pointer, */ /* holding the window's current */ /* details (e.g. visible area) or */ /* NULL if this is not known; */ /* */ /* 1 to scroll left, else right; */ /* */ /* 1 to page left/right, else 0; */ /* */ /* 1 to move one line, else 0; */ /* */ /* An amount to scroll by, ignored */ /* unless the above two parameters */ /* are zero; */ /* */ /* Pointer to an int, in which 1 is */ /* written if the window didn't */ /* shift scroll position as it was */ /* at the limit of its work area, */ /* else 0 is written. */ /*************************************************/ _kernel_oserror * browser_scroll_page_h(browser_data * b, WimpOpenWindowBlock * o, int dir, int page, int line, int amount, int * limit) { int scrollby; WimpGetWindowStateBlock open; _kernel_oserror * e; /* Work out the WimpOpenWindowBlock if NULL was passed in */ if (!o) { open.window_handle = b->window_handle; e = wimp_get_window_state(&open); if (e) return e; o = (WimpOpenWindowBlock *) &open; } if (limit) { /* If required, see if we're at the limit of the scroll position */ BBox extent; int scrollmax; e = window_get_extent(0, b->self_id, &extent); if (e) return e; scrollmax = !dir ? extent.xmin - (o->visible_area.xmax - o->visible_area.xmin) : 0; if (scrollmax == o->xscroll) *limit = 1; else *limit = 0; } /* Work out how much to scroll by */ if (page) scrollby = o->visible_area.ymax - o->visible_area.ymin; else if (line) scrollby = 42; else scrollby = amount; /* If greater than zero, move the page */ if (scrollby > 0) { if (dir) scrollby = -scrollby; o->xscroll += scrollby; return wimp_open_window(o); } return NULL; } /*************************************************/ /* browser_scroll_page_by_key() */ /* */ /* Scrolls a page according to a given key code. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the page; */ /* */ /* Key code from the Wimp (to define */ /* left / right, page movement or */ /* line movement, etc.); */ /* */ /* Pointer to an int, in which 1 is */ /* written if the window didn't */ /* shift scroll position as it was */ /* at the limit of its work area, */ /* else 0 is written (this only */ /* applies for vertical scrolling - */ /* the written value will always be */ /* 0 if moving horizontally). */ /*************************************************/ _kernel_oserror * browser_scroll_page_by_key(browser_data * b, int key, int * limit) { int page = 0, line = 0, dir = 0; WimpGetWindowStateBlock s; _kernel_oserror * e; s.window_handle = b->window_handle; e = wimp_get_window_state(&s); if (e) return e; if ( key == akbd_UpK || key == akbd_PageUpK || key == akbd_LeftK || key == akbd_HomeK || key == akbd_UpK + akbd_Ctl || key == akbd_UpK + akbd_Ctl + akbd_Sh || key == akbd_LeftK + akbd_Ctl || key == akbd_LeftK + akbd_Ctl + akbd_Sh ) dir = 1; if ( key == akbd_PageUpK || key == akbd_PageDownK ) page = 1; if ( key == akbd_UpK || key == akbd_DownK || key == akbd_LeftK || key == akbd_RightK || key == akbd_UpK + akbd_Ctl + akbd_Sh || key == akbd_DownK + akbd_Ctl + akbd_Sh || key == akbd_LeftK + akbd_Ctl + akbd_Sh || key == akbd_RightK + akbd_Ctl + akbd_Sh ) line = 1; if ( key == akbd_LeftK || key == akbd_RightK || key == akbd_LeftK + akbd_Ctl || key == akbd_LeftK + akbd_Ctl + akbd_Sh || key == akbd_RightK + akbd_Ctl || key == akbd_RightK + akbd_Ctl + akbd_Sh ) { /* For left/right key presses, want to make sure as */ /* the caller that the input focus is not in a */ /* writable icon, or if it is, the effect of having */ /* the page scroll as the caret tries to move has */ /* been taken into account (e.g. the caret is known */ /* to be at the start/end of the writable's text). */ if (limit) *limit = 0; return browser_scroll_page_h(b, (WimpOpenWindowBlock *) &s, dir, page, line, (!(page + line) ? 0x1000000 : 0), NULL); } else { e = browser_scroll_page_v(b, (WimpOpenWindowBlock *) &s, dir, page, line, (!(page + line) ? 0x1000000 : 0), limit); if (e) return e; /* For Home, make sure the page is scrolled to the far left */ if (!page && !line && dir) return browser_scroll_page_h(b, (WimpOpenWindowBlock *) &s, 1, 0, 0, 0x1000000, NULL); // This seems generally undesirable... // // /* For End, make sure the page is scrolled to the far right */ // // if (!page && !line && !dir) return browser_scroll_page_h(b, // (WimpOpenWindowBlock *) &s, // 0, // 0, // 0, // 0x1000000, // NULL); } return NULL; } /*************************************************/ /* browser_find_first_selectable() */ /* */ /* Examines the visible area of a given browser */ /* window to see if a selectable token is */ /* present in it, and returns the address of the */ /* token if so. The token returned may not be */ /* fully visible - the caller must use */ /* browser_check_visible on the returned token */ /* if the token must be fully visible. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the window; */ /* */ /* A WimpGetWindowStateBlock pointer */ /* holding details of the window */ /* (if NULL because the caller */ /* doesn't have this information to */ /* hand, the function will work it */ /* out); */ /* */ /* Direction to search in; 1 for */ /* bottom right to top left, 0 for */ /* top left to bottom right. */ /* */ /* Returns: Pointer to the token to select, */ /* or NULL if none are visible. */ /*************************************************/ HStream * browser_find_first_selectable(browser_data * b, WimpGetWindowStateBlock * s, int dir) { HStream * token_null = NULL; WimpGetWindowStateBlock state; if (!b) return NULL; /* Get the window state if it wasn't given */ if (!s) { state.window_handle = b->window_handle; if (wimp_get_window_state(&state)) return NULL; s = &state; } /* Find the selectable, marking that nothing is to be */ /* skipped (*(&token_null) = NULL). */ if (!dir) return browser_find_selectable_top_r(b, b->cell, &token_null, 0, s); else return browser_find_selectable_bot_r(b, b->cell, &token_null, 0, s); } /*************************************************/ /* browser_find_another_selectable() */ /* */ /* Takes a given selected token, and finds the */ /* previous or next selectable, optionally */ /* constraining the search to moving to a new */ /* line, rather than allowing to stay on the */ /* same one. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the window; */ /* */ /* Pointer to the currently selected */ /* token; */ /* */ /* Direction to search in; 1 for */ /* bottom right to top left, 0 for */ /* top left to bottom right; */ /* */ /* Constraint (1 to allow tokens on */ /* the same line, else must go to a */ /* new line). */ /* */ /* Returns: Pointer to the new token that */ /* should be selected, or NULL for */ /* none. */ /*************************************************/ HStream * browser_find_another_selectable(browser_data * b, HStream * current, int dir, int constrain) { WimpGetWindowStateBlock state; HStream * current_rec = current; /* Get the browser window's state */ if (!b) return NULL; state.window_handle = b->window_handle; if (wimp_get_window_state(&state)) return NULL; /* Ensure that the selected token in current_rec is */ /* at the top of the tokens representing the same */ /* selectable. */ tokenutils_anchor_range(b, current_rec, ¤t_rec, NULL); /* Call the relevant function to find the item */ if (!dir) return browser_find_selectable_top_r(b, b->cell, ¤t_rec, 0, &state); else return browser_find_selectable_bot_r(b, b->cell, ¤t_rec, 0, &state); } /*************************************************/ /* browser_find_selectable_top_r() */ /* */ /* Recursive back-end to the 'from top-left' */ /* call to browser_find_another_selectable and */ /* browser_find_first_selectable. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the window; */ /* */ /* Pointer to a reformat_cell struct */ /* holding the lines to scan; */ /* */ /* Pointer to a pointer to the */ /* currently selected token (this */ /* will be written to) - if there is */ /* no such token, this should point */ /* to a word holding NULL; */ /* */ /* y origin of that cell, in OS */ /* units from the top left of the */ /* whole page; */ /* */ /* A WimpGetWindowStateBlock pointer */ /* holding details of the window; */ /* */ /* Returns: As browser_find_first_selectable. */ /*************************************************/ HStream * browser_find_selectable_top_r(browser_data * b, reformat_cell * cell, HStream ** current, int y_origin, WimpGetWindowStateBlock * s) { HStream * t = NULL; HStream * last = NULL; int exit; int ytop, ybot, htop, hbot; int line, chunk, chunkmax; if (!cell || !cell->nlines || !cell->ldata || !cell->cdata) return NULL; #ifdef TRACE if (tl & (1u<<22)) Printf("browser_find_selectable_top_r: Proceeding for %p, cell %p\n", b, cell); #endif /* Work out where the visible page region starts and ends */ if (!controls.swap_bars) { htop = toolbars_button_height(b) + toolbars_url_height(b); hbot = toolbars_status_height(b); } else { htop = toolbars_status_height(b); hbot = toolbars_button_height(b) + toolbars_url_height(b); } if (htop) htop += wimpt_dy(); if (hbot) hbot += wimpt_dy(); ytop = s->yscroll - htop; ybot = s->yscroll - (s->visible_area.ymax - s->visible_area.ymin) + hbot; #ifdef TRACE if (tl & (1u<<22)) Printf("browser_find_selectable_top_r: ytop, ybot: -%d, -%d\n",-ytop,-ybot); #endif /* Go through the cell's chunks, getting tokens for as long */ /* as the chunk holding lines are in the visible area. */ line = 0; exit = 0; /* Find the line visible at the top of the window. This can be slow if */ /* there are a lot of lines, so use a Cunning Plan - divide the window */ /* extent (take the y coordinate of the last line) by the number of */ /* lines to get the average line height, use this and the ytop coord */ /* to have a good guess at the line, then move a short distance up or */ /* down to get the actual correct line. */ /* */ /* Of course, this only works for the main line list. It could be */ /* adjusted for tables, but there's no time to do it right now... */ /* Tables are rarely large enough to need it anyway. */ if (cell != b->cell) { while (line < cell->nlines && y_origin + cell->ldata[line].y > ytop) line ++; } else { int extent = cell->ldata[cell->nlines - 1].y; int average = extent / cell->nlines; int startat; if (average) startat = ytop / average; else startat =0; if (startat < 0) startat = 0; if (startat >= cell->nlines) startat = cell->nlines - 1; if (y_origin + cell->ldata[startat].y > ytop) { int lastline = -1; line = startat; while (line < cell->nlines && y_origin + cell->ldata[line].y > ytop) lastline = line, line ++; if (lastline >= 0) line = lastline; else line = startat; } else { line = startat; while (line >= 0 && y_origin + cell->ldata[line].y <= ytop) line --; if (line < 0) line = 0; } } /* Proceed until the line visible at the bottom */ while (line < cell->nlines && y_origin + cell->ldata[line].y + cell->ldata[line].h > ybot && !exit) { #ifdef TRACE if (tl & (1u<<22)) Printf("browser_find_selectable_top_r: Line %d\n",line); #endif chunk = cell->ldata[line].chunks; chunkmax = cell->ldata[line].n + chunk; while (chunk < chunkmax && !exit) { /* Find the token represented by this chunk */ t = cell->cdata[chunk].t; if (!t) break; #ifdef TRACE if (tl & (1u<<22)) { Printf("browser_find_selectable_top_r: Chunk %d of %d, token %p\n", chunk, chunkmax, t); if (t == last) Printf("browser_find_selectable_top_r: t = last, so won't deal with this chunk\n"); } #endif /* Several chunks can represent the same token - don't want to */ /* deal with it multiple times, though. */ if (t != last) { last = t; /* Exit successfully (exit = 1, t != NULL) if the token can be */ /* selected and is visible. */ if (!*current && CanBeSelected(t) && browser_check_visible(b, s, t)) { #ifdef TRACE if (tl & (1u<<22)) Printf("browser_find_selectable_top_r: ** Found %p **\n", t); #endif exit = 1; break; } /* If we're supposed to select the token after *current - i.e. *current */ /* is still not NULL - then find the first token making up that same */ /* link. If this matches *current, we've found that link - so, clear */ /* *current and skip past the link. */ if (*current && CanBeSelected(t)) { HStream * top, * bot; tokenutils_anchor_range(b, t, &top, &bot); if (top == *current) { *current = NULL; tokenutils_find_token(b, cell, bot, NULL, NULL, &line, &chunk); } } /* Deal with tables */ if (t->tagno == TAG_TABLE) { table_stream * table = (table_stream *) t; table_row * row = table->List; table_headdata * head = NULL; HStream * found = NULL; reformat_cell * c = NULL; reformat_cell * cellarray = table->cells; int cellmax = table->ColSpan * table->RowSpan; int cellindex; int xorg, yorg; #ifdef TRACE if (tl & (1u<<22)) Printf("browser_find_selectable_top_r: Dealing with table\n"); #endif /* Proceed if the cell array can be found */ if (cellarray) { while (row && !found) { head = row->List; while ( head && !found && head->RowOffs < table->RowSpan && head->ColOffs < table->ColSpan ) { switch (head->Tag) { case TagTableData: case TagTableHead: { /* Find the reformat_cell structure for this table cell */ cellindex = head->RowOffs * table->ColSpan + head->ColOffs; if (cellindex < cellmax) { c = &cellarray[cellindex]; #ifdef TRACE if (tl & (1u<<22)) Printf("browser_find_selectable_top_r: Cell index %d, cell %p\n",cellindex,cellarray); #endif convert_pair_to_os(c->x, c->y, &xorg, &yorg); /* Recursive call to look at cell contents */ found = browser_find_selectable_top_r(b, c, current, cell->ldata[line].y + cell->ldata[line].h + y_origin + yorg, s); #ifdef TRACE if (tl & (1u<<22)) Printf("browser_find_selectable_top_r: Cell index %d, cell %p - Found: %p\n",cellindex, cellarray, found); #endif } } /* Closure of 'switch (head->Tag)' */ } head = head->Next; /* Closure of 'while (head && ...)' */ } row = row->Next; /* Closure of 'while (row && ...)' */ } /* Closure of 'if (cellarray)' */ } if (found) { t = found; exit = 1; #ifdef TRACE if (tl & (1u<<22)) Printf("browser_find_selectable_top_r: ** Found %p in table, exitting **\n",t); #endif break; } #ifdef TRACE else if (tl & (1u<<22)) Printf("browser_find_selectable_top_r: Nothing found in table\n"); #endif } /* Closure of 'if (t != last)' */ } chunk ++; /* Closure of 'while' loop scanning chunks */ } line ++; /* Closure of 'while' loop scanning the lines */ } /* If exit wasn't forced, we scanned the whole of the visible line list */ /* for this cell and didn't find a visible, selectable token. */ if (!exit) t = NULL; /* Return the found value, be it NULL or a valid selectable token */ #ifdef TRACE if (tl & (1u<<22)) Printf("browser_find_selectable_top_r: -- Returning %p --\n", t); #endif return t; } /*************************************************/ /* browser_find_selectable_bot_r() */ /* */ /* Recursive back-end to the 'from bottom-right' */ /* call to browser_find_another_selectable and */ /* browser_find_first_selectable. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the window; */ /* */ /* Pointer to a reformat_cell struct */ /* holding the lines to scan; */ /* */ /* Pointer to a pointer to the */ /* currently selected token (this */ /* will be written to) - if there is */ /* no such token, this should point */ /* to a word holding NULL; */ /* */ /* y origin of that cell, in OS */ /* units from the top left of the */ /* whole page; */ /* */ /* A WimpGetWindowStateBlock pointer */ /* holding details of the window. */ /* */ /* Returns: As browser_find_first_selectable. */ /*************************************************/ HStream * browser_find_selectable_bot_r(browser_data * b, reformat_cell * cell, HStream ** current, int y_origin, WimpGetWindowStateBlock * s) { HStream * t = NULL; HStream * last = NULL; int exit; int ytop, ybot, htop, hbot; int line, chunk, chunkmin; if (!cell || !cell->nlines || !cell->ldata || !cell->cdata) return NULL; #ifdef TRACE if (tl & (1u<<22)) Printf("browser_find_selectable_bot_r: Proceeding for %p, cell %p\n", b, cell); #endif /* Work out where the visible page region starts and ends */ if (!controls.swap_bars) { htop = toolbars_button_height(b) + toolbars_url_height(b); hbot = toolbars_status_height(b); } else { htop = toolbars_status_height(b); hbot = toolbars_button_height(b) + toolbars_url_height(b); } if (htop) htop += wimpt_dy(); if (hbot) hbot += wimpt_dy(); ytop = s->yscroll - htop; ybot = s->yscroll - (s->visible_area.ymax - s->visible_area.ymin) + hbot; #ifdef TRACE if (tl & (1u<<22)) Printf("browser_find_selectable_bot_r: ytop, ybot: -%d, -%d\n",-ytop,-ybot); #endif /* Go through the cell's chunks, getting tokens for as long */ /* as the chunk holding lines are in the visible area. */ line = cell->nlines - 1; exit = 0; /* Find the line visible at the top of the window. This can be slow if */ /* there are a lot of lines, so use a Cunning Plan - divide the window */ /* extent (take the y coordinate of the last line) by the number of */ /* lines to get the average line height, use this and the ybot coord */ /* to have a good guess at the line, then move a short distance up or */ /* down to get the actual correct line. */ /* */ /* Of course, this only works for the main line list. It could be */ /* adjusted for tables, but there's no time to do it right now... */ /* Tables are rarely large enough to need it anyway. */ if (cell != b->cell) { while (line >= 0 && y_origin + cell->ldata[line].y + cell->ldata[line].h < ybot) line --; } else { int extent = cell->ldata[cell->nlines - 1].y; int average = extent / cell->nlines; int startat = ybot / average + 1; if (startat < 0) startat = 0; if (startat >= cell->nlines) startat = cell->nlines - 1; if (y_origin + cell->ldata[startat].y + cell->ldata[startat].h < ybot) { int lastline = -1; line = startat; while (line >= 0 && y_origin + cell->ldata[line].y + cell->ldata[line].h < ybot) lastline = line, line --; if (lastline >= 0) line = lastline; else line = startat; } else { line = startat; while (line < cell->nlines && y_origin + cell->ldata[line].y + cell->ldata[line].h >= ybot) line ++; if (line >= cell->nlines) line = cell->nlines - 1; } } /* Proceed until the line visible at the top */ while (line >= 0 && y_origin + cell->ldata[line].y < ytop && !exit) { #ifdef TRACE if (tl & (1u<<22)) Printf("browser_find_selectable_bot_r: Line %d\n",line); #endif chunkmin = cell->ldata[line].chunks; chunk = cell->ldata[line].n + chunkmin - 1; while (chunk >= chunkmin && !exit) { /* Find the token represented by this chunk */ t = cell->cdata[chunk].t; if (!t) break; #ifdef TRACE if (tl & (1u<<22)) { Printf("browser_find_selectable_bot_r: Chunk %d of %d minimum, token %p\n", chunk, chunkmin, t); if (t == last) Printf("browser_find_selectable_bot_r: t = last, so won't deal with this chunk\n"); } #endif /* Several chunks can represent the same token - don't want to */ /* deal with it multiple times, though. */ if (t != last) { last = t; /* Exit successfully (exit = 1, t != NULL) if the token can be */ /* selected and is visible. */ if (!*current && CanBeSelected(t) && browser_check_visible(b, s, t)) { #ifdef TRACE if (tl & (1u<<22)) Printf("browser_find_selectable_bot_r: ** Found %p **\n", t); #endif exit = 1; break; } /* If we're supposed to select the token after *current - i.e. *current */ /* is still not NULL - then, having made sure that the current token */ /* 't' represents an anchor, find the first token making up that same */ /* link. If this matches *current, we've found that link - so, clear */ /* *current and skip over the link. */ if (*current && CanBeSelected(t)) { HStream * top, * bot; tokenutils_anchor_range(b, t, &top, &bot); if (top == *current) { *current = NULL; tokenutils_find_token(b, cell, top, &line, &chunk, NULL, NULL); } } /* Deal with tables */ if (t->tagno == TAG_TABLE) { table_stream * table = (table_stream *) t; table_row * row = NULL; table_headdata * head = NULL; HStream * found = NULL; reformat_cell * c = NULL; reformat_cell * cellarray = table->cells; int cellmax = table->ColSpan * table->RowSpan; int cellindex; int xorg, yorg; #ifdef TRACE if (tl & (1u<<22)) Printf("browser_find_selectable_bot_r: Dealing with table\n"); #endif /* Proceed if the cell array can be found */ if (cellarray) { /* Start on the last row and work backwards */ row = table->List; while (row && row->Next) row = row->Next; while (row && !found) { head = row->List; /* Start on the last cell and work backwards */ while (head && head->Next) head = head->Next; while ( head && !found && head->RowOffs < table->RowSpan && head->ColOffs < table->ColSpan ) { switch (head->Tag) { case TagTableData: case TagTableHead: { /* Find the reformat_cell structure for this table cell */ cellindex = head->RowOffs * table->ColSpan + head->ColOffs; if (cellindex < cellmax) { c = &cellarray[cellindex]; #ifdef TRACE if (tl & (1u<<22)) Printf("browser_find_selectable_bot_r: Cell index %d, cell %p\n",cellindex,cellarray); #endif convert_pair_to_os(c->x, c->y, &xorg, &yorg); /* Recursive call to look at cell contents */ found = browser_find_selectable_bot_r(b, c, current, cell->ldata[line].y + cell->ldata[line].h + y_origin + yorg, s); #ifdef TRACE if (tl & (1u<<22)) Printf("browser_find_selectable_bot_r: Cell index %d, cell %p - Found: %p\n",cellindex, cellarray, found); #endif } } /* Closure of 'switch (head->Tag)' */ } head = head->Prev; /* Closure of 'while (head && ...)' */ } row = row->Prev; /* Closure of 'while (row && ...)' */ } /* Closure of 'if (cellarray)' */ } if (found) { t = found; exit = 1; #ifdef TRACE if (tl & (1u<<22)) Printf("browser_find_selectable_bot_r: ** Found %p in table, exitting **\n",t); #endif break; } #ifdef TRACE else if (tl & (1u<<22)) Printf("browser_find_selectable_bot_r: Nothing found in table\n"); #endif } /* Closure of 'if (t != last)' */ } chunk --; /* Closure of 'while' loop scanning chunks */ } line --; /* Closure of 'while' loop scanning the lines */ } /* If exit wasn't forced, we scanned the whole of the visible line list */ /* for this cell and didn't find a visible, selectable token. */ if (!exit) t = NULL; /* Return the found value, be it NULL or a valid selectable token */ #ifdef TRACE if (tl & (1u<<22)) Printf("browser_find_selectable_bot_r: -- Returning %p --\n", t); #endif return t; } /*************************************************/ /* browser_move_selection() */ /* */ /* Moves the selected item up or down (to a */ /* previous link, picture or forms item, or to a */ /* next item). */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the selected item; */ /* */ /* Key press from a keyboard handler */ /* (used to determine the direction */ /* and distance of motion). */ /* */ /* Returns: 1 if the keypress was used for */ /* something, else 0 (e.g. there are */ /* no more objects to select). */ /*************************************************/ int browser_move_selection(browser_data * b, int key) { int page = 0, line = 0, dir = 0, horiz = 0; WimpGetWindowStateBlock s; HStream * new = NULL; HStream * first_selected = NULL; HStream * last_selected = NULL; browser_data * ancestor = utils_ancestor(b); browser_data * owner; owner = ancestor->selected_owner; if (!owner) owner = b; if (!choices.keyboard_ctrl) return 0; if (ancestor->in_image_map || b->in_image_map) { /* If browser_navigate_map returns a non-zero value, */ /* drop the keypress through. */ key = browser_navigate_map(b, key); if (!key) return 1; } s.window_handle = b->window_handle; if (wimp_get_window_state(&s)) return 0; /* Ctrl+Shift+Arrows nudges the page scroll position, */ /* it doesn't move any selections. */ if ( key == akbd_UpK + akbd_Ctl + akbd_Sh || key == akbd_DownK + akbd_Ctl + akbd_Sh || key == akbd_LeftK + akbd_Ctl + akbd_Sh || key == akbd_RightK + akbd_Ctl + akbd_Sh ) return 0; /* Otherwise, work out which direction to move in, is this line or */ /* page movement, etc. */ if ( key == akbd_UpK || key == akbd_PageUpK || key == akbd_LeftK || key == akbd_HomeK || key == akbd_UpK + akbd_Ctl || key == akbd_LeftK + akbd_Ctl ) dir = 1; if ( key == akbd_PageUpK || key == akbd_PageDownK ) page = 1; if ( key == akbd_UpK || key == akbd_DownK || key == akbd_LeftK || key == akbd_RightK ) line = 1; if ( key == akbd_LeftK || key == akbd_RightK || key == akbd_LeftK + akbd_Ctl || key == akbd_RightK + akbd_Ctl ) horiz = 1; /* If there's no selected token, or there is but it's not visible, */ /* then reselect as appropriate from the tokens (if any) currently */ /* visible on the page. */ if (ancestor->selected) tokenutils_anchor_range(owner, ancestor->selected, &first_selected, &last_selected); else first_selected = last_selected = NULL; if ( line && ( !ancestor->selected || ( ancestor->selected && !browser_check_visible(owner, &s, first_selected) && !browser_check_visible(owner, &s, last_selected) ) ) ) { ancestor->selected = NULL; /* No redraw problems as the conditions above ensure selected token is not in visible area now */ ancestor->selected_owner = NULL; new = browser_find_first_selectable(owner, &s, dir); } /* Alternatively, if there's a selected token, move up or down from it */ else if (line && ancestor->selected) { new = browser_find_another_selectable(owner, ancestor->selected, dir, horiz); /* If up/down is used but the next item is not visible on screen so */ /* the page would scroll, want to first ensure that everything on */ /* the current line is selected - i.e. try moving horizontally. */ if ( new && !horiz && !browser_check_visible(owner, &s, new) ) new = browser_find_another_selectable(owner, ancestor->selected, dir, 1); } if (new) { // if ( // tokenutils_within_distance(owner, // new, // ancestor->selected, // s.visible_area.ymax - // s.visible_area.ymin - // toolbars_status_height(b) - // toolbars_url_height(b) - // toolbars_button_height(b)) // ) if (browser_check_visible(owner, &s, new)) { /* If there are any forms menus open, close them */ if (menusrc == Menu_Form) form_abandon_menu(); browser_ensure_visible(owner, &s, new); browser_select_token(owner, new, 0); /* Update the status bar */ toolbars_update_status(owner, Toolbars_Status_LinkTo); /* Move inside writable elements */ if ( form_token_cursor_editable(owner, new) ) form_click_field(owner, new, ((key == akbd_DownK || key == akbd_RightK) ? 0 : 2), 0, 0); /* Turn the pointer off, and reset the check to see if */ /* the user has moved it manually. */ mouse_pointer_off(); mouse_force_unused(); return 1; } else { /* This is so that if there's another selectable, but it's off screen, */ /* the user doesn't suddenly start scrolling left / right, only up or */ /* down to reach the next selectable. */ if (horiz) return 1; } } return 0; } /*************************************************/ /* browser_navigate_map() */ /* */ /* Moves around an image map by keyboard control */ /* with the machine single tasking during a key */ /* autorepeat. */ /* */ /* To exit this 'mode', drop off the image map */ /* or follow a link with Return. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the browser the image */ /* map lies in; */ /* */ /* Wimp keycode used to move in it. */ /* */ /* Returns: 0 if the pointer is still in the */ /* map, else a Wimp key code saying */ /* which way it fell off. */ /*************************************************/ static int browser_navigate_map(browser_data * b, int key) { HStream * tp; int map_x = -1; int map_y = -1; WimpGetPointerInfoBlock p; if (!wimp_get_pointer_info(&p)) { tp = browser_get_pointer_token(b, &p, NULL, NULL); if ( tp && redraw_selected(b, tp) && ( ( (tp->style & IMG) && (tp->type & TYPE_ISMAP) ) || ( tp->tagno == TAG_INPUT && HtmlINPUTtype(tp) == inputtype_IMAGE ) || ( tp->type & TYPE_ISCLIENTMAP ) ) ) { int last_move = 4; int last_key, start_key; int time_delay, time_now; int repeat_delay, repeat_rate; HStream * current; _swix(OS_Byte, _INR(0,1) | _OUT(1), 121, /* Keyboard scan */ 0, /* Scan all keys */ &start_key); /* Record the key(s) being pressed (not the Wimp keycode) */ mouse_watch_pointer_control(0); mouse_pointer_on(); b->pointer_over = NULL; browser_pointer_check(0, NULL,NULL, b); /* Get the keyboard repeat rate and delay */ _swix(OS_Byte, _INR(0, 2) | _OUTR(1, 2), 196, /* Read auto-repeat delay */ 0, 255, &repeat_delay, &repeat_rate); /* Read the current time into time_delay and set it negative, */ /* to flag that the repeat delay part of autorepeat is active */ _swix(OS_ReadMonotonicTime, _OUT(0), &time_delay); time_delay = -time_delay + 1; /* +1 as a '>' is used in the comparisson later, not '>='. */ do { /* Move according to key used */ if (key == akbd_UpK) p.y += last_move; if (key == akbd_DownK) p.y -= last_move; if (key == akbd_LeftK) p.x -= last_move; if (key == akbd_RightK) p.x += last_move; if (key == akbd_Sh + akbd_UpK) p.y += last_move * 3; if (key == akbd_Sh + akbd_DownK) p.y -= last_move * 3; if (key == akbd_Sh + akbd_LeftK) p.x -= last_move * 3; if (key == akbd_Sh + akbd_RightK) p.x += last_move * 3; map_x = p.x, map_y = p.y; mouse_to(p.x, p.y, 1); /* Loop round waiting for the keyboard repeat delay or rate */ do { _swix(OS_Byte, _INR(0,1) | _OUT(1), 121, /* Keyboard scan */ 0, /* Scan all keys */ &last_key); _swix(OS_ReadMonotonicTime, _OUT(0), &time_now); /* If time_delay is negative, wait for the repeat delay */ if (time_delay < 0) { if (time_now + time_delay > repeat_delay) time_delay = time_now; } /* Otherwise, wait for the repeat rate */ else { if (time_now - time_delay > repeat_rate) time_delay = time_now, last_move += 1; } } while (time_now != time_delay && last_key != 255 && last_key == start_key); /* Make sure that next time round the loop, the exit condition above doesn't */ /* immediately activate, giving about 1 centisecond of very fast repeats... */ time_delay = time_now - 1; /* Hence the use of '>' rather than '>=' above */ _swix(OS_Byte, _INR(0,1), 21, 0); /* Flush keyboard buffer */ current = browser_get_pointer_token(b, &p, NULL, NULL); /* Keep going whilst we're over the same token and keys are being pressed */ } while (tp == current && last_key != 255 && last_key == start_key); /* If we're on the same token, exit, claiming the key press. */ /* Else, allow the press to drop through (so you'll go on to */ /* select the next/previous token if running off the edge of */ /* the image). */ if (tp == current) return 0; /* Otherwise, reenable pointer watching etc. and allow the */ /* key press to drop to through. */ } /* (This code also executes if the pointer isn't */ /* over an image map any more - e.g. the user */ /* moved the mouse away). */ mouse_set_pointer_shape(Mouse_Shape_Normal); debounce_keypress(); /* Clear the flag saying an image map is selected */ /* and sort out mouse pointer issues */ { browser_data * ancestor = utils_ancestor(b); mouse_pointer_off(); b->in_image_map = ancestor->in_image_map = 0; b->pointer_over = ancestor->pointer_over = NULL; browser_pointer_check(0, NULL,NULL, b); mouse_watch_pointer_control(1); } return key; } return 0; } /*************************************************/ /* browser_fetch_url() */ /* */ /* Looks in the browser_data structure given to */ /* the function and returns the URL that is */ /* currently being fetched, or NULL for none. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the fetch. */ /* */ /* Returns: Pointer to the URL string or NULL */ /* if there is no fetch URL present. */ /*************************************************/ char * browser_fetch_url(browser_data * b) { return b->urlfdata; } /*************************************************/ /* browser_current_url() */ /* */ /* Returns a pointer to the current URL being */ /* displayed in a browser window, unless there */ /* is none, in which case the fetch URL is */ /* given; the pointer may still be NULL though. */ /* */ /* Parameters: A pointer to a browser_data */ /* structure relevant to the window. */ /* */ /* Returns: Pointer to the displayed URL, or */ /* the fetch URL if none is */ /* displayed. */ /*************************************************/ char * browser_current_url(browser_data * b) { /* NB, should this function ever become more complex than this, */ /* note that external callers rely on it NOT corrupting the */ /* 'tokens' buffer used by lookup_token or lookup_choice - i.e. */ /* this function may not call these. */ return b->urlddata; } /*************************************************/ /* browser_current_title() */ /* */ /* Examines an HStream list for a given browser */ /* trying to find a TITLE tag; if it finds it, */ /* a pointer to the title text is returned. */ /* */ /* Parameters: A pointer to a browser_data */ /* structure relevant to the HStream */ /* list. */ /* */ /* Returns: Pointer to the page title, or */ /* NULL if none is found. */ /*************************************************/ char * browser_current_title(browser_data * b) { HStream * current = b->stream; while (current) { if (ISHEAD(current) && current->tagno == TAG_TITLE && current->text) return current->text; current = current->next; } return NULL; } /*************************************************/ /* browser_destroy_source() */ /* */ /* Pass a browser_data structure pointer, and if */ /* this points to a block of memory holding */ /* fetched HTML source, free up that block. */ /* */ /* Parameters: A pointer to a browser_data */ /* structure relevant to the source. */ /*************************************************/ void browser_destroy_source(browser_data * b) { if (b->source) { #ifdef TRACE if (tl & (1u<<12)) Printf("browser_destroy_source: flex_free block %p which held page source\n",&b->source); flexcount -= flex_size((flex_ptr) &b->source); if (tl & (1u<<14)) Printf("** flexcount: %d\n",flexcount); #endif flex_free((flex_ptr) &b->source); b->source = NULL; } } /*************************************************/ /* browser_pointer_entering() */ /* */ /* Called when the pointer goes over a browser */ /* window. Installs a null event handler to */ /* watch over the pointer's position relative to */ /* links on the page. */ /* */ /* Parameters are as standard for a Wimp event */ /* handler. */ /*************************************************/ int browser_pointer_entering(int eventcode, WimpPollBlock * block, IdBlock * idb, void * handle) { browser_data * b = NULL; /* If dragging, don't want to know about this at all (at least */ /* for now, when drags only correspond to resizing frames) */ if (drag_in_progress) return 0; /* Quick sanity check (this can and does happen - Toolbox oddities) */ if (!idb->self_id) return 0; /* Is this a browser we know about? */ ChkError(toolbox_get_client_handle(0, idb->self_id, (void *) &b)); if (is_known_browser(b)) register_null_claimant(Wimp_ENull, (WimpEventHandler *) browser_pointer_check, b); pointer_is_over = idb->self_id; return 0; } /*************************************************/ /* browser_pointer_leaving() */ /* */ /* Called when the pointer goes out of a browser */ /* window. Deinstalls a null event handler that */ /* watched over the pointer's position relative */ /* to links on the page. */ /* */ /* Note that objects may be deleted and this */ /* function wouldn't be called, so anything that */ /* goes in here should be echoed somewhere in */ /* windows_close_browser. */ /* */ /* Parameters are as standard for a Wimp event */ /* handler. */ /*************************************************/ int browser_pointer_leaving(int eventcode, WimpPollBlock * block, IdBlock * idb, void * handle) { browser_data * b = NULL; /* Quick sanity check (as for browser_pointer_entering) */ if (!idb->self_id) return 0; #ifdef TRACE /* If the pointer is leaving, we should know what it was over */ if (idb->self_id != pointer_is_over) { erb.errnum = Utils_Error_Custom_Normal; sprintf(erb.errmess, "Existing pointer_is_over ID %08x doesn't match ID %08x given to browser_pointer_leaving", pointer_is_over, idb->self_id); show_error_ret(&erb); } #endif ChkError(toolbox_get_client_handle(0, idb->self_id, (void *) &b)); if (is_known_browser(b)) { deregister_null_claimant(Wimp_ENull, (WimpEventHandler *) browser_pointer_check, b); if (!drag_in_progress) { mouse_set_pointer_shape(Mouse_Shape_Normal); if (mouse_pointer_is_on()) toolbars_cancel_status(b, Toolbars_Status_LinkTo); b->pointer_over = NULL; } pointer_is_over = 0; return 1; } pointer_is_over = 0; return 0; } /*************************************************/ /* browser_pointer_over_deleted() */ /* */ /* Checks to see if the pointer is over an */ /* object that has just been deleted, and if */ /* so, deregisters any pointer-related event */ /* handlers associated with it. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the window / object. */ /*************************************************/ void browser_pointer_over_deleted(browser_data * b) { if (pointer_is_over && pointer_is_over == b->self_id) { deregister_null_claimant(Wimp_ENull, (WimpEventHandler *) browser_pointer_check, b); mouse_set_pointer_shape(Mouse_Shape_Normal); } } /*************************************************/ /* browser_pointer_check() */ /* */ /* Checks the pointer position relative to any */ /* links on the page; if it is over one, */ /* the pointer shape is changed and the status */ /* bar updated. Alternatively, it is changed */ /* back to the normal pointer shape and the */ /* status bar put back to its Ready state. */ /* */ /* This function will also change the pointer to */ /* a shape indicating frame borders may be */ /* dragged to resize them, where appropriate. */ /* */ /* Parameters are as for a standard Wimp event */ /* handler, though only browser_data * handle is */ /* of interest (it points to a browser_data */ /* struct relevant to the window in question). */ /* */ /* The function may be used as a NULL handler, */ /* so never return 1 from it (you'll claim the */ /* null event - this is Bad). */ /*************************************************/ int browser_pointer_check(int eventcode, WimpPollBlock * b, IdBlock * idb, browser_data * handle) { int changed; HStream * tp = NULL; WimpGetPointerInfoBlock p; /* If we're dragging, exit */ if (drag_in_progress) return 0; if (wimp_get_pointer_info(&p)) return 0; /* If this browser has children, we must be over frame borders */ if (handle->nchildren && !choices.fixed_pointer) { int row, col; if (!frames_find_pointer_in_frameset(handle, p.x, p.y, &row, &col, NULL, NULL, 0)) { if (row > 0 && col < 0) mouse_set_pointer_shape(Mouse_Shape_UD); else if (row < 0 && col > 0) mouse_set_pointer_shape(Mouse_Shape_LR); else if (row > 0 && col > 0) mouse_set_pointer_shape(Mouse_Shape_UDLR); else if (row == -2 || col == -2) mouse_set_pointer_shape(Mouse_Shape_NoResize); else mouse_set_pointer_shape(Mouse_Shape_Normal); } return 0; } /* Find the token that the pointer is over */ tp = browser_get_pointer_token(handle,&p,NULL,NULL); if ( choices.highlight_links && tp && CanBeSelected(tp) && mouse_pointer_is_on() && !redraw_selected(handle, tp) ) { browser_clear_selection(handle, 0); browser_select_token(handle, tp, 0); } /* Is this an image map? If so, have the coordinates of the pointer */ /* over the map changed? */ if (tp && (tp->type & (TYPE_ISCLIENTMAP | TYPE_ISMAP))) { int nx, ny; /* Find out which pixel we clicked on */ ChkError(image_return_click_offset(handle, tp, &p, &nx, &ny)); if (nx != handle->map_x || ny != handle->map_y) { handle->map_x = nx; handle->map_y = ny; changed = 1; } else changed = 0; } /* Otherwise, work out if we've changed from the token address. */ else changed = (tp != handle->pointer_over); /* Sometimes pointer_over has to be zeroed because a token */ /* list is getting freed, e.g. at the beginning of a new */ /* fetch. In this case, we need to always restore the */ /* pointer shape if tp is NULL. */ if (tp == NULL) changed = 1; /* If this isn't the same as the token the pointer */ /* was last recorded as being over... */ if (changed) { handle->pointer_over = tp; if (!choices.fixed_pointer) { /* If the Choices say the pointer can change shape, */ /* and the pointer is over an identified token, */ /* set the pointer to ptr_link. Else restore the */ /* pointer to its normal shape. */ if ( tp && ( (tp->type & TYPE_ISCLIENTMAP) || (ISLINK(tp)) ) ) { int dealt_with = 0; /* Client side map */ if (tp->type & TYPE_ISCLIENTMAP) { char * url; /* Find out what we're over - affects the pointer shape, you see... */ csim_return_info(handle, tp, handle->map_x, handle->map_y, &url, NULL, NULL); /* Use a Link pointer for areas defined in the map */ if (url && *url) { mouse_set_pointer_shape(Mouse_Shape_Link); dealt_with = 1; } } /* For anything which isn't a client side map, we must have */ /* an anchor. */ if (!dealt_with && tp->anchor && *tp->anchor) { /* If this is a server side map, use the Map pointer */ if (tp->type & TYPE_ISMAP) { mouse_set_pointer_shape(Mouse_Shape_Map); } /* Otherwise, use the normal Link pointer */ else { mouse_set_pointer_shape(Mouse_Shape_Link); } dealt_with = 1; } /* If we've still not worked out what this is, cancel any */ /* LinkTo message (e.g. over a client side image map with */ /* no alternative URLs and not on any area defined by the */ /* map itself). */ if (!dealt_with) { handle->pointer_over = NULL; mouse_set_pointer_shape(Mouse_Shape_Normal); } } else if ( tp && tp->tagno == TAG_INPUT && HtmlINPUTtype(tp) == inputtype_IMAGE ) mouse_set_pointer_shape(Mouse_Shape_Link); else { handle->pointer_over = NULL; mouse_set_pointer_shape(Mouse_Shape_Normal); } } /* If the mouse pointer is on, then update the status bar */ /* to reflect the link it is over (or cancel a LinkTo */ /* message if it has moved off a link), */ if (mouse_pointer_is_on()) { if (handle->pointer_over) toolbars_update_status(handle, Toolbars_Status_LinkTo); else toolbars_cancel_status(handle, Toolbars_Status_LinkTo); } } return 0; } /*************************************************/ /* browser_get_pointer_token() */ /* */ /* Returns the token number that the pointer is */ /* over (if any) and an X and Y offset into */ /* the line chunk representing that token. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* for the window the pointer is */ /* over; */ /* */ /* Pointer to a block describing */ /* the pointer condition; */ /* */ /* Pointer to an int, in which the X */ /* offset into the chunk is placed; */ /* Same for the Y offset. */ /* */ /* Returns: Address of the token the pointer */ /* is over, or NULL for unknown / */ /* none. */ /* */ /* Assumes: The pointers to the ints for the */ /* X and Y offsets *can* be NULL. */ /*************************************************/ HStream * browser_get_pointer_token(browser_data * b, WimpGetPointerInfoBlock * p, int * ox, int * oy) { WimpGetWindowStateBlock state; reformat_cell * cell = b->cell; /* If we're dragging, return NULL */ if (drag_in_progress) return NULL; /* No point proceeding if there are no lines */ if (!cell->ldata) return NULL; /* No point searching the tokens if we're not over the right window and */ /* then only in the display area of that window. */ if (b->window_handle != p->window_handle || p->icon_handle < -1) return NULL; /* Return 0 if there's an error getting the window's state information */ state.window_handle = p->window_handle; if (wimp_get_window_state(&state)) return NULL; return browser_get_pointer_token_r(b, cell, p, &state, ox, oy); } /*************************************************/ /* browser_get_pointer_token_r() */ /* */ /* Recursive back-end to */ /* browser_get_pointer_token. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* for the window the pointer is */ /* over; */ /* */ /* Pointer to a reformat_cell struct */ /* holding the line/chunk data to */ /* check against the pointer */ /* position; */ /* */ /* Pointer to a block describing */ /* the pointer condition; */ /* */ /* A WimpGetWindowStateBlock pointer */ /* where the block contains details */ /* of the browser window; */ /* */ /* Pointer to an int, in which the X */ /* offset into the chunk is placed; */ /* */ /* Same for the Y offset. */ /* */ /* Returns: As browser_get_pointer_token. */ /* */ /* Assumes: As browser_get_pointer_token. */ /*************************************************/ static HStream * browser_get_pointer_token_r(browser_data * b, reformat_cell * cell, WimpGetPointerInfoBlock * p, WimpGetWindowStateBlock * state, int * ox, int * oy) { int x, y, line; /* Convert the pointer's screen x and y coordinates to window coordinatess */ x = coords_x_toworkarea(p->x, (WimpRedrawWindowBlock *) state); y = coords_y_toworkarea(p->y, (WimpRedrawWindowBlock *) state); /* Find the line that the pointer is over */ line = browser_line_at_y(b, cell, y); if (line < 0) return 0; /* Convert the x coordinate to millipoints */ convert_to_points(x, &x); if (ox) *ox = 0; if (oy) *oy = 0; /* Proceed only if the line has some chunks associated with it */ if (cell->ldata[line].n) { int cx, n, i; HStream * tp; /* Find the token's address for the first chunk on the line, */ /* work out where its X coordinate should be and compare */ /* this to the pointer X. If the pointer X is less than the */ /* line X, the pointer lies to the left of all the chunks so */ /* exit here with 0. */ tp = cell->cdata[cell->ldata[line].chunks].t; cx = redraw_start_x(b, cell, tp, line); convert_to_points(cx, &cx); if (x < cx) return 0; /* For each chunk, take the calculated left hand coordinate */ /* for the whole line and add the width of each chunk until */ /* the pointer X lies to the left of the calculated coord. */ n = 0; i = cell->ldata[line].n - 1; while (i >= 0 && x > (cx + cell->cdata[cell->ldata[line].chunks + n].w)) { cx += cell->cdata[cell->ldata[line].chunks + n].w; n++; i--; } /* If a chunk was found that the pointer is over... */ if (i >= 0) { BBox box; /* Get the address of the token corresponding to the chunk */ tp = cell->cdata[cell->ldata[line].chunks + n].t; #ifdef TRACE if (tl & (1u<<3)) { static tracelastchunk = -1; if (tracelastchunk != cell->ldata[line].chunks + n) { Printf("Chunk : %d, token: %d\n",cell->ldata[line].chunks + n,cell->cdata[cell->ldata[line].chunks + n].t); Printf("Text : '%s'\n",tp->text); Printf("Style : %p\n",(void *) tp->style); if (ISLINK(tp)) { Printf("Link : '%s'\n",tp->anchor); Printf("Target: '%s'\n",tp->target); } tracelastchunk = cell->ldata[line].chunks + n; } } #endif /* If the token represents a table... */ if (tp->tagno == TAG_TABLE) { /* In this case there are table streams hung from d->cdata */ table_stream * table = (table_stream *) tp; table_row * row = NULL; table_headdata * head = NULL; reformat_cell * cellarray = table->cells; reformat_cell * c; WimpGetWindowStateBlock s = *state; HStream * over = NULL; int tablx, tably; int lineh; int cellindex; int cellcount = 0; int cellmax = table->ColSpan * table->RowSpan; /* Only proceed if there are table cells to redraw */ if (cellarray) { /* Get the bottom left of the table in tablx, tably, */ /* in window coords, units of millipoints. */ tablx = cx; convert_to_points(cell->ldata[line].y, &tably); /* Get the line height in millipoints */ convert_to_points(cell->ldata[line].h, &lineh); /* Table cells aren't scrolled! */ s.xscroll = s.yscroll = 0; /* Start going through the rows */ row = table->List; while (row && !over && cellcount < cellmax) { head = row->List; while (head && !over && cellcount < cellmax) { switch (head->Tag) { case TagTableData: case TagTableHead: { cellindex = head->RowOffs * table->ColSpan + head->ColOffs; if (cellindex < cellmax) { c = &cellarray[cellindex]; /* No point proceeding without lines to scan */ if (c->nlines) { /* Set the visible area BBox to be that of the current cell */ /* (this will be in millipoint window coords) */ s.visible_area.xmin = tablx + c->x; s.visible_area.ymin = tably + c->y + lineh - c->cellheight; s.visible_area.xmax = s.visible_area.xmin + c->cellwidth - 1; s.visible_area.ymax = s.visible_area.ymin + c->cellheight - 1; /* Convert the above to OS units, then to screen coords */ convert_box_to_os(&s.visible_area, &s.visible_area); coords_box_toscreen(&s.visible_area, (WimpRedrawWindowBlock *) state); /* Recursive call */ over = browser_get_pointer_token_r(b, c, p, &s, ox, oy); /* If something has been found, may need to fill in ox and oy */ if (over) { /* If an address of an int to return Y information to was given... */ if (oy) { /* Set the int to hold the distance from the top of the line */ /* that the pointer was at. */ convert_to_os(lineh, oy); *oy -= (p->y - s.visible_area.ymin); if (*oy < 0) *oy = 0; if (*oy > s.visible_area.ymax) *oy = s.visible_area.ymax; } /* Similarly for X information, get the X offset into the chunk */ if (ox) { *ox = p->x - s.visible_area.xmin; if (ISLINK(over)) *ox -= over->maxlen * 2; if (*ox < 0) *ox = 0; if (*ox > s.visible_area.xmax) *ox = s.visible_area.xmax; } } /* Closure of 'if (c->nlines)' */ } /* Closure of 'if (cellindex < cellmax)' */ } /* Closure of specific 'case' item */ } break; /* Closure of 'switch (head->Tag)' */ } cellcount++; head = head->Next; /* Closure of 'while (head)' */ } row = row->Next; /* Closure of 'while (row)' */ } /* Closure of 'if (cellarray)' */ } return over; /* Closure of 'if (tp->tagno == TAG_TABLE)' */ } /* If the token represents an image... */ else if ( (tp->style & IMG) || (ISOBJECT(tp)) || ( tp->tagno == TAG_INPUT && HtmlINPUTtype(tp) == inputtype_IMAGE ) ) { int brdr = 0; if (ISOBJECT(tp)) reformat_get_object_size(b, tp, &box); else reformat_get_image_size (b, tp, &box); /* Correct for the border size if the image is a link, */ /* or if this is an Object. */ if (ISLINK(tp) && (tp->style & IMG)) brdr = tp->maxlen * 2; else if (ISOBJECT(tp)) brdr = HtmlOBJECTborder(tp) * 2; if (brdr) { box.xmin += brdr; box.ymin += brdr; box.xmax -= brdr; box.ymax -= brdr; } } else { /* The token does not represent an image, and so is text. */ box.xmin = 0; /* Convert the width of the line chunk to OS units */ convert_to_os(cell->cdata[cell->ldata[line].chunks + n].w, &box.xmax); /* Set the Y coordinates of the BBox structure to the base */ /* line and base line plus height of the chunk. */ box.ymin = -cell->ldata[line].b; box.ymax = cell->ldata[line].h - cell->ldata[line].b; } /* If an address of an int to return Y information to was given... */ if (oy) { /* Set the int to hold the distance from the bottom of the line */ /* that the pointer was at. */ *oy = (cell->ldata[line].y + cell->ldata[line].b + box.ymax) - y; if (*oy < 0) *oy = 0; if (*oy >= (box.ymax - box.ymin)) *oy = box.ymax - box.ymin - 1; } /* Similarly for X information, get the X offset into the chunk */ if (ox) { *ox = x - cx; convert_to_os(*ox, ox); if (ISLINK(tp)) *ox -= tp->maxlen * 2; if (*ox < 0) *ox = 0; if (*ox >= box.xmax) *ox = box.xmax - 1; } /* Return the token address */ return cell->cdata[cell->ldata[line].chunks + n].t; } } return NULL; } /*************************************************/ /* browser_line_at_y() */ /* */ /* Returns the line number for a given y window */ /* coordinate. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the window; */ /* */ /* Pointer to a reformat_cell struct */ /* holding the line information; */ /* */ /* The y coordinate. */ /* */ /* Returns: The line number in which the Y */ /* coordinate lives, or -1 for none */ /* / an error. */ /*************************************************/ int browser_line_at_y(browser_data * b, reformat_cell * cell, int y) { int l; if (!cell) cell = b->cell; /* Find a line who's y coordinate is lower than the given one, */ /* i.e. its baseline is the first one of all lines in th cell */ /* that lies below that coordinate. */ for (l = 0; (l <= cell->nlines) && (cell->ldata[l].y > y); l++); /* Either we have run out of lines, or the top of the line we */ /* found also lies below the given coordinate, in which case the */ /* coordinate does not lie within that line and we should not */ /* return its number. */ if (l >= cell->nlines || cell->ldata[l].y + cell->ldata[l].h < y) return -1; return l; } /*************************************************/ /* browser_top_line() */ /* */ /* Returns the line number displayed at the top */ /* of the visible area of the browser window. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the window; */ /* */ /* Pointer to a word into which the */ /* address of a reformat_cell will */ /* be returned - the given line lies */ /* in this cell; */ /* */ /* A WimpGetWindowStateBlock pointer */ /* holding details of the browser */ /* window; */ /* */ /* 1 if the line must be wholly */ /* visible, else 0 if it may be just */ /* partially visible. */ /* */ /* Returns: Directly, the number of the line */ /* displayed at the top of the */ /* window, or -1 for none / an */ /* error. */ /* */ /* Assumes: That *none* of the parameter */ /* pointers are NULL. */ /*************************************************/ int browser_top_line(browser_data * b, reformat_cell ** ret_cell, WimpGetWindowStateBlock * s, int fully_visible) { if (!ret_cell) return -1; *ret_cell = NULL; return browser_top_line_r(b, b->cell, ret_cell, s, fully_visible); } /*************************************************/ /* browser_top_line_r() */ /* */ /* Recursive back-end to browser_top_line. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the window; */ /* */ /* Pointer to a reformat_cell struct */ /* holding the cells to scan; */ /* */ /* Pointer to a word into which the */ /* address of a reformat_cell will */ /* be returned - the given line lies */ /* in this cell; */ /* */ /* A WimpGetWindowStateBlock pointer */ /* holding details of the browser */ /* window; */ /* */ /* 1 if the line must be wholly */ /* visible, else 0 if it may be just */ /* partially visible. */ /* */ /* Returns: As browser_top_line. */ /* */ /* Assumes: As browser_top_line. */ /*************************************************/ static int browser_top_line_r(browser_data * b, reformat_cell * cell, reformat_cell ** ret_cell, WimpGetWindowStateBlock * s, int fully_visible) { int y, l, htop; if (!cell) cell = b->cell; if (!cell->nlines || !cell->ldata || !cell->cdata) return -1; if (!controls.swap_bars) htop = toolbars_button_height(b) + toolbars_url_height(b); else htop = toolbars_status_height(b); if (htop) htop += wimpt_dy(); y = s->yscroll - htop; l = 0; /* Find the line */ while ( l < cell->nlines && cell->ldata[l].y + (fully_visible ? cell->ldata[l].h : 0) > y ) l++; /* If l >= number of lines, nothing was found */ if (l >= cell->nlines) return -1; else { HStream * first_token = cell->cdata[cell->ldata[l].chunks].t; *ret_cell = cell; /* Otherwise, is there a table in the line? */ if (first_token->tagno == TAG_TABLE) { table_stream * table = (table_stream *) first_token; table_row * row = NULL; table_headdata * head = NULL; reformat_cell * c = NULL; reformat_cell * cellarray = table->cells; int cellmax = table->ColSpan * table->RowSpan; int cellcount = 0; int cellindex; /* Proceed if the cell array can be found */ if (cellarray) { row = table->List; while (row && cellcount < cellmax) { head = row->List; while (head && cellcount < cellmax) { switch (head->Tag) { case TagTableData: case TagTableHead: { /* Find the reformat_cell structure for this table cell */ cellindex = head->RowOffs * table->ColSpan + head->ColOffs; if (cellindex < cellmax) { c = &cellarray[cellindex]; /* If it has lines, recursively call this function to find the */ /* cell line at the top of the window. */ if (c->nlines) { l = browser_top_line_r(b, c, ret_cell, s, fully_visible); /* If a line can be found inside this cell then return it immediately, */ /* rather than worrying about the other cells. Otherwise, continue */ /* scanning cells. Since table cells are arranged left right, top to */ /* bottom, and we are looking for the top line, this will work OK. */ if (l >= 0) return l; } } } break; } cellcount ++; head = head->Next; /* Closure of 'while (head && ...)' */ } row = row->Next; /* Closure of 'while (row && ...)' */ } /* Closure of 'if (cellarray)' */ } /* Closure of 'if (first_token->tagno == TAG_TABLE)' */ } /* Closure of 'else' case for 'if (l >= cell->nlines)' */ } return l; } /*************************************************/ /* browser_bottom_line() */ /* */ /* Returns the line number displayed at the */ /* bottom of the visible area of the browser */ /* window. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the window; */ /* */ /* Pointer to a word into which the */ /* address of a reformat_cell will */ /* be returned - the given line lies */ /* in this cell; */ /* */ /* A WimpGetWindowStateBlock pointer */ /* holding details of the browser */ /* window; */ /* */ /* 1 if the line must be wholly */ /* visible, else 0 if it may be just */ /* partially visible. */ /* */ /* Returns: Directly, the number of the line */ /* displayed at the bottom of the */ /* window, or -1 for none / an */ /* error. */ /* */ /* Assumes: That none of the parameter */ /* pointers are NULL. */ /*************************************************/ int browser_bottom_line(browser_data * b, reformat_cell ** ret_cell, WimpGetWindowStateBlock * s, int fully_visible) { if (!ret_cell) return -1; *ret_cell = NULL; return browser_bottom_line_r(b, b->cell, ret_cell, s, fully_visible); } /*************************************************/ /* browser_bottom_line_r() */ /* */ /* Recursive back-end to browser_bottom_line. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the window; */ /* */ /* Pointer to a reformat_cell struct */ /* holding the cells to scan; */ /* */ /* Pointer to a word into which the */ /* address of a reformat_cell will */ /* be returned - the given line lies */ /* in this cell; */ /* */ /* A WimpGetWindowStateBlock pointer */ /* holding details of the browser */ /* window; */ /* */ /* 1 if the line must be wholly */ /* visible, else 0 if it may be just */ /* partially visible. */ /* */ /* Returns: As browser_bottom_line. */ /* */ /* Assumes: As browser_bottom_line. */ /*************************************************/ static int browser_bottom_line_r(browser_data * b, reformat_cell * cell, reformat_cell ** ret_cell, WimpGetWindowStateBlock * s, int fully_visible) { int y, l, hbot; if (!cell) cell = b->cell; if (!cell->nlines || !cell->ldata || !cell->cdata) return -1; if (!controls.swap_bars) hbot = toolbars_status_height(b); else hbot = toolbars_button_height(b) + toolbars_url_height(b); if (hbot) hbot += wimpt_dy(); y = s->yscroll - (s->visible_area.ymax - s->visible_area.ymin) - hbot; l = cell->nlines - 1; /* Find the line */ while ( l >= 0 && cell->ldata[l].y + (fully_visible ? 0 : cell->ldata[l].h) < y ) l--; /* If l < 0, nothing was found */ if (l < 0) return -1; else { HStream * first_token = cell->cdata[cell->ldata[l].chunks].t; *ret_cell = cell; /* Otherwise, is there a table in the line? */ if (first_token->tagno == TAG_TABLE) { table_stream * table = (table_stream *) first_token; table_row * row = NULL; table_headdata * head = NULL; reformat_cell * c = NULL; reformat_cell * cellarray = table->cells; int cellmax = table->ColSpan * table->RowSpan; int cellcount = 0; int cellindex; /* Proceed if the cell array can be found */ if (cellarray) { /* Since we're finding the bottom line and table cells are arranged */ /* left to right, top to bottom, we need to start at the last row */ /* of the last column. */ row = table->List; while (row && row->Next) row = row->Next; while (row && cellcount < cellmax) { head = row->List; while (head && head->Next) head = head->Next; while (head && cellcount < cellmax) { switch (head->Tag) { case TagTableData: case TagTableHead: { /* Find the reformat_cell structure for this table cell */ cellindex = head->RowOffs * table->ColSpan + head->ColOffs; if (cellindex < cellmax) { c = &cellarray[cellindex]; /* If it has lines, recursively call this function to find the */ /* cell line at the top of the window. */ if (c->nlines) { l = browser_bottom_line_r(b, c, ret_cell, s, fully_visible); /* If a line can be found inside this cell then return it immediately, */ /* rather than worrying about the other cells. Otherwise, continue */ /* scanning cells. Since table cells are arranged left right, top to */ /* bottom, and we are looking for the top line, this will work OK. */ if (l >= 0) return l; } } } break; } cellcount ++; // head = head->Prev; /* Closure of 'while (head && ...)' */ } // row = row->Prev; /* Closure of 'while (row && ...)' */ } /* Closure of 'if (cellarray)' */ } /* Closure of 'if (first_token->tagno == TAG_TABLE)' */ } /* Closure of 'else' case for 'if (l >= cell->nlines)' */ } return l; } /*************************************************/ /* browser_redraw_border() */ /* */ /* Does a series of Wimp_ForceRedraw calls that */ /* cause a border of 4 OS units around a given */ /* image to be redrawn. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the image; */ /* */ /* Pointer to a token representing */ /* the image. */ /*************************************************/ static _kernel_oserror * browser_redraw_border(browser_data * b, HStream * token) { BBox r1; int more, x, y, w, h; _kernel_oserror * e; WimpGetWindowStateBlock state; WimpRedrawWindowBlock redraw; /* Find the position of the image */ e = reformat_get_image_size(b, token, &r1); if (e) return e; w = r1.xmax - r1.xmin; h = r1.ymax - r1.ymin; state.window_handle = redraw.window_handle = b->window_handle; e = wimp_get_window_state(&state); if (e) return e; /* Find the x and y coordinates of the image's bottom left hand corner */ if (image_get_token_image_position(b, token, &x, &y)) return NULL; /* Work out the screen coordinates of the image, with an extra */ /* amount account for the selection border. */ r1.xmin = x - 4; r1.ymin = y - 4; r1.xmax = r1.xmin + w + 8; r1.ymax = r1.ymin + h + 8; redraw.xscroll = state.xscroll; redraw.yscroll = state.yscroll; /* Bottom edge */ redraw.visible_area.xmin = r1.xmin, redraw.visible_area.ymin = r1.ymin; redraw.visible_area.xmax = r1.xmax, redraw.visible_area.ymax = r1.ymin + 4; e = wimp_update_window(&redraw, &more); if (e) return e; if (more) { e = redraw_draw(b, &redraw, 0, token); if (e) return e; } /* Left edge */ redraw.visible_area.xmin = r1.xmin, redraw.visible_area.ymin = r1.ymin; redraw.visible_area.xmax = r1.xmin + 4, redraw.visible_area.ymax = r1.ymax; e = wimp_update_window(&redraw, &more); if (e) return e; if (more) { e = redraw_draw(b, &redraw, 0, token); if (e) return e; } /* Top edge */ redraw.visible_area.xmin = r1.xmin, redraw.visible_area.ymin = r1.ymax - 4; redraw.visible_area.xmax = r1.xmax, redraw.visible_area.ymax = r1.ymax; e = wimp_update_window(&redraw, &more); if (e) return e; if (more) { e = redraw_draw(b, &redraw, 0, token); if (e) return e; } /* Right edge */ redraw.visible_area.xmin = r1.xmax - 4, redraw.visible_area.ymin = r1.ymin; redraw.visible_area.xmax = r1.xmax, redraw.visible_area.ymax = r1.ymax; e = wimp_update_window(&redraw, &more); if (e) return e; if (more) return redraw_draw(b, &redraw, 0, token); return NULL; } /*************************************************/ /* browser_update() */ /* */ /* Updates a window contents, using calls to */ /* Wimp_UpdateWindow (so this can be used for */ /* animations, etc., as the Wimp won't clear */ /* the redraw area first). */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the window; */ /* */ /* Pointer to a RedrawWindow block, */ /* with details of the area to */ /* redraw within it; */ /* */ /* 1 to not draw backgrounds, or 0 */ /* to allow them to be drawn; */ /* */ /* 0 to draw tokens normally, else a */ /* pointer to a token which is not */ /* to have its contents redrawn, */ /* except as a selection indicator */ /* (see redraw_draw for more). */ /*************************************************/ _kernel_oserror * browser_update(browser_data * b, WimpRedrawWindowBlock * r, int noback, HStream * nocontent) { int more; r->window_handle = b->window_handle; wimp_update_window(r, &more); if (more) return redraw_draw(b, r, noback, nocontent); return NULL; } /*************************************************/ /* browser_update_token() */ /* */ /* Redraws a given token, trying to minimise any */ /* flicker as the redraw is done. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the token to redraw; */ /* */ /* The token address; */ /* */ /* 1 to not draw backgrounds, or 0 */ /* to allow them to be drawn; */ /* */ /* 0 to draw tokens normally, else a */ /* pointer to a token which is not */ /* to have its contents redrawn, */ /* except as a selection indicator */ /* (see redraw_draw for more). */ /* */ /* Returns: Pointer to a _kernel_oserror */ /* struct, which is NULL unless the */ /* actual redraw call fails - so if */ /* the given token does not appear */ /* to be represented by any line, */ /* the routine fails silently. */ /*************************************************/ _kernel_oserror * browser_update_token(browser_data * b, HStream * token, int noback, HStream * nocontent) { token_path * path = NULL; int first = -1; int fchnk = -1; int last = -1; int depth; reformat_cell * cell = NULL; _kernel_oserror * e = NULL; int x, y; /* Find the range of lines the token spans */ depth = tokenutils_line_range(b, token, &first, &fchnk, &last, NULL, &path); /* Find out the x and y offset of the cell the */ /* token lies in. */ tokenutils_token_offset(b, path, &x, &y); /* If there are valid entries in the token_path structure, */ /* need to find out what line list browser_update_token_r */ /* should be called on. Otherwise, it's the main line */ /* list. */ cell = tokenutils_find_cell(b->cell, depth, path); if (path) free (path); if (cell) { e = browser_update_token_r(b, cell, token, first, fchnk, last, x, y, noback, nocontent); } else { e = browser_update_token_r(b, b->cell, token, first, fchnk, last, 0, 0, noback, nocontent); } return e; } /*************************************************/ /* browser_update_token_r() */ /* */ /* Recursive back-end to browser_update_token. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the token to redraw; */ /* */ /* Pointer to a reformat_cell struct */ /* which holds the token or a table */ /* holding the token; */ /* */ /* First line to check in the cell; */ /* */ /* First chunk on the first line; */ /* */ /* Last line to check in the cell; */ /* */ /* The token address; */ /* */ /* Cell origin x (window coords); */ /* */ /* Cell origin y (window coords); */ /* */ /* 1 to not draw backgrounds, or 0 */ /* to allow them to be drawn; */ /* */ /* 0 to draw tokens normally, else a */ /* pointer to a token which is not */ /* to have its contents redrawn, */ /* except as a selection indicator */ /* (see redraw_draw for more). */ /* */ /* Returns: As browser_update_token. */ /*************************************************/ static _kernel_oserror * browser_update_token_r(browser_data * b, reformat_cell * cell, HStream * token, int first, int chunk, int last, int base_x, int base_y, int noback, HStream * nocontent) { int l, widen; WimpRedrawWindowBlock r; _kernel_oserror * e; int x, y; if (!cell) cell = b->cell; /* Items that may have borders drawn around them need to have */ /* the redraw area widened to cope with that. */ if ((token->style & FORM) || (token->style & IMG)) widen = 4; else widen = 0; if (first >= 0) { for (l = first; l <= last; l++) { /* Get the y coordinate of the bottom of the line into y */ y = base_y + cell->ldata[l].y; /* Get the left hand x coordinate in x - the given token for the */ /* first line, or the left hand edge for subsequent ones. */ if (l == first) x = base_x + redraw_token_x(b, cell, token, l); else x = base_x + redraw_start_x(b, cell, cell->cdata[cell->ldata[l].chunks].t, l); /* Fill in the redraw block's visible area field */ r.visible_area.xmin = x - 4; r.visible_area.ymin = y - widen; /* xmax is up to the last chunk's right hand edge for anything but */ /* the last line, when we go as far as chunks that use this token. */ if (l == last) { int w = 0, c, mc; /* For the first line, the chunk to start counting on will be */ /* defined by the given token; otherwise, it will be the first */ /* one on the line. */ if (l == first) c = chunk; else c = cell->ldata[l].chunks; /* Count up to either a chunk which doesn't use the given token */ /* or is the last chunk on the line */ mc = cell->ldata[l].chunks + cell->ldata[l].n; while (c < mc && cell->cdata[c].t == token) w += cell->cdata[c].w, c++; /* Convert to OS units and add to the left hand edge */ convert_to_os(w, &w); r.visible_area.xmax = w + r.visible_area.xmin + 8; } else { /* Width of the last chunk */ convert_to_os(cell->cdata[cell->ldata[l].chunks + cell->ldata[l].n - 1].w, &r.visible_area.xmax); /* Add the left hand edge of that chunk */ r.visible_area.xmax += base_x + redraw_chunk_x(b, cell, cell->ldata[l].chunks + cell->ldata[l].n - 1, l) + 8; } /* ymax is the bottom line plus the line height */ r.visible_area.ymax = r.visible_area.ymin + cell->ldata[l].h + widen * 2 - (widen == 0); e = browser_update(b, &r, noback, nocontent); if (e) return e; } } return NULL; } /*************************************************/ /* browser_update_bottom() */ /* */ /* Redraws the browser window, from a given */ /* work area y coordinate downwards. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the window to redraw; */ /* */ /* The y coordinate to redraw from. */ /*************************************************/ _kernel_oserror * browser_update_bottom(browser_data * b, int top_y) { int more; _kernel_oserror * e; WimpRedrawWindowBlock r; #ifdef TRACE if (tl & (1u<<9)) Printf("\nbrowser_update_bottom: Called, -top_y = %d\n",-top_y); #endif /* Fill in the visible area and scroll info from the */ /* GetWindowState call */ r.window_handle = b->window_handle; /* Due to the way the veneer works and the structure is defined, */ /* the visible_area here is actually the redraw block. The call */ /* to wimp_update_window will exit eventually through the */ /* Wimp_GetRectangle SWI, which fills in 'r' properly. */ r.visible_area.xmin = -0x1000001; /* Big numbers to ensure the whole */ r.visible_area.xmax = 0x1000000; /* work area width is redrawn */ r.visible_area.ymin = -0x1000001; /* Go right to the bottom */ r.visible_area.ymax = top_y; /* The redraw loop itself */ wimp_update_window(&r,&more); /* 'r' now holds information that more sensibly relates to its field names... */ if (more) { e = redraw_draw(b, &r, 0, 0); if (e) return e; } #ifdef TRACE else if (tl & (1u<<9)) Printf("\nbrowser_update_bottom: Nothing to redraw\n"); if (tl & (1u<<9)) Printf("\nbrowser_update_bottom: Successful\n"); #endif return NULL; } /*************************************************/ /* browser_highlight_token() */ /* */ /* Redraws a given token in a highlighted state; */ /* see redraw_token_colour in Redraw.c for the */ /* colour it will be drawn as. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the token; */ /* */ /* The token address. */ /*************************************************/ void browser_highlight_token(browser_data * b, HStream * token) { int noback = 0; /* Ensure all current highlighting is cleared and remember */ /* that this token has been highlighted in the browser_data */ /* structure. */ browser_clear_highlight(b, 1); b->highlight=token; /* If the token represents a link, that link may be made of several */ /* tokens (e.g. a heading where font sizes were used to make the */ /* first letters bigger, or where there is a style change in the */ /* middle to highlight some search result for a Web search engine, */ /* say). So need to find the first of the list of tokens with the */ /* same anchor, and the last. */ if ((token->style & A) && token->anchor) { HStream * top = NULL; HStream * end = NULL; tokenutils_anchor_range(b, token, &top, &end); /* Now redraw between those two tokens */ if (top && end) { while (top != end->next) { #ifndef ANTI_TWITTER if ( b->background_image < 0 || ( (top->style & IMG) && ( ( b->show_foreground && image_token_plot_started(b, top) ) || b->displayed == Display_External_Image ) ) ) noback = 1; else noback = 0; #endif b->highlight = top; browser_update_token(b, top, noback, 0); top = top->next; } } else { /* Something went wrong above, or there is only one token */ /* for this link - so use quicker redraw code. */ browser_update_token(b, token, noback, 0); } } /* If the token is not a link, just redraw it, as asked. */ else browser_update_token(b, token, noback, 0); } /*************************************************/ /* browser_clear_highlight() */ /* */ /* Removes a highlight shown using the */ /* browser_highlight_token function, with an */ /* optional delay before doing so. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the token; */ /* */ /* 1 to wait first, else 0. */ /*************************************************/ void browser_clear_highlight(browser_data * b, int wait) { int noback = 0; /* If nothing's highlighted, don't need to do anything else! */ if (b->highlight) { HStream * token; /* Clear the highlight, possibly waiting 20 centiseconds first. */ token = b->highlight; b->highlight = NULL; if (wait) { int start_time, now; _swix(OS_ReadMonotonicTime, _OUT(0), &start_time); now = start_time; while (now - start_time <= 20) _swix(OS_ReadMonotonicTime, _OUT(0), &now); } /* For details on this, see browser_highlight_token. Basically, */ /* may need to redraw several tokens if the highlighted one was */ /* a link, if they all represent the same link. */ if ((token->style & A) && token->anchor) { HStream * top = NULL; HStream * end = NULL; tokenutils_anchor_range(b, token, &top, &end); /* Now redraw between those two tokens */ if (top && end) { while (top != end->next) { #ifndef ANTI_TWITTER if ( b->background_image < 0 || ( (top->style & IMG) && ( ( b->show_foreground && image_token_plot_started(b, top) ) || b->displayed == Display_External_Image ) ) ) noback = 1; else noback = 0; #endif browser_update_token(b, top, noback, 0); top = top->next; } } else { /* Something went wrong above, or there is only one token */ /* for this link - so use quicker redraw code. */ browser_update_token(b, token, noback, 0); } } else browser_update_token(b, token, noback, 0); } } /*************************************************/ /* browser_flash_token() */ /* */ /* 'Flashes' a token, by highlighting it with */ /* browser_highlight_token and then clearing */ /* the highlight after a short delay. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the token; */ /* */ /* The token address. */ /*************************************************/ void browser_flash_token(browser_data * b, HStream * token) { browser_highlight_token(b, token); browser_clear_highlight(b, 1); } /*************************************************/ /* browser_select_token() */ /* */ /* Redraws a given token in a selected state. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the token; */ /* */ /* The token address; */ /* */ /* 1 to scroll the window if needed */ /* to keep the token in the visible */ /* area, else 0. */ /*************************************************/ void browser_select_token(browser_data * b, HStream * token, int visible) { int noback = 0; browser_data * ancestor = utils_ancestor(b); /* Ensure any selected token is deselected, and remember */ /* that this token has been marked as selected in the */ /* browse_data structure. */ if (ancestor->selected_owner) browser_clear_selection(ancestor->selected_owner, 0); ancestor->selected = token; ancestor->selected_owner = b; /* The selected frame is always the one with the highlighted token in it */ if (ancestor->selected_frame != b) { frames_highlight_frame(b); ancestor->selected_frame = b; } /* If asked to, ensure the token is visible */ if (visible) { WimpGetWindowStateBlock s; s.window_handle = b->window_handle; if (!wimp_get_window_state(&s)) browser_ensure_visible(b, &s, token); } /* If the token represents a link, that link may be made of several */ /* tokens (e.g. a heading where font sizes were used to make the */ /* first letters bigger, or where there is a style change in the */ /* middle to highlight some search result for a Web search engine, */ /* say). So need to find the first of the list of tokens with the */ /* same anchor, and the last. */ if ((token->style & A) && token->anchor) { HStream * top = NULL; HStream * end = NULL; tokenutils_anchor_range(b, token, &top, &end); /* Now redraw between those two tokens */ if (top && end) { while (top != end->next) { #ifndef ANTI_TWITTER if ( b->background_image < 0 || ( (top->style & IMG) && ( ( b->show_foreground && image_token_plot_started(b, top) ) || b->displayed == Display_External_Image ) ) ) noback = 1; else noback = 0; #endif b->selected = top; if ( b->show_foreground && (top->style & IMG) && (ISLINK(top)) && !top->maxlen && /* maxlen=0 means there's no border already there */ image_token_plot_started(b, top) ) browser_redraw_border(b, top); else browser_update_token(b, top, noback, 0); top = top->next; } } else { /* Something went wrong above, or there is only one token */ /* for this link - so use quicker redraw code. */ if ( b->show_foreground && (token->style & IMG) && (ISLINK(token)) && !top->maxlen && /* maxlen=0 means there's no border already there */ image_token_plot_started(b, token) ) browser_redraw_border(b, token); else browser_update_token(b, token, noback, 0); } } /* If the token is not a link, just redraw it, as asked. */ else browser_update_token(b, token, noback, 0); return; } /*************************************************/ /* browser_clear_selection() */ /* */ /* Removes a selection shown using the */ /* browser_select_token function, with an */ /* delay before doing so. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the token; */ /* */ /* 1 to wait first, else 0. */ /*************************************************/ void browser_clear_selection(browser_data * b, int wait) { int noback = 0; browser_data * ancestor = utils_ancestor(b); browser_data * owner; if (!ancestor) return; /* If nothing's selected, don't need to do anything else! */ if (ancestor->selected) { HStream * token; /* Clear the selection, possibly waiting 20 centiseconds first. */ token = ancestor->selected; owner = ancestor->selected_owner; ancestor->selected = NULL; ancestor->selected_owner = NULL; if (wait) { int start_time, now; _swix(OS_ReadMonotonicTime, _OUT(0), &start_time); now = start_time; while (now - start_time <= 20) _swix(OS_ReadMonotonicTime, _OUT(0), &now); } /* For details on this, see browser_select_token. Basically, may */ /* need to redraw several tokens if the selected one was a link, */ /* if they all represent the same link. */ if ((token->style & A) && token->anchor) { HStream * top = NULL; HStream * end = NULL; tokenutils_anchor_range(owner, token, &top, &end); /* Now redraw between those two tokens */ if (top && end) { while (top != end->next) { #ifndef ANTI_TWITTER /* If using anti-twitter redraws, always redraw the background. */ /* Otherwise, redraw it unless this is a text item (well, neither */ /* an image nor a form element) as for text items, can just */ /* replot the text. */ if ( !(top->style & IMG) && !(top->style & FORM) && owner->background_image < 0 ) noback = 1; else noback = 0; #endif /* Images which are links and are having only the selection marker */ /* redrawn go through a special routine. */ if ( owner->show_foreground && (top->style & IMG) && (ISLINK(top)) && !top->maxlen && /* maxlen=0 means there's no border already there */ image_token_plot_started(owner, top) ) browser_redraw_border(owner, top); else browser_update_token(owner, top, noback, 0); top = top->next; } } else { /* Something went wrong above, or there is only one token */ /* for this link - so use quicker redraw code. */ if ( owner->show_foreground && (token->style & IMG) && (ISLINK(token)) && !top->maxlen && /* maxlen=0 means there's no border already there */ image_token_plot_started(owner, token) ) browser_redraw_border(owner, token); else browser_update_token(owner, token, noback, 0); } } else browser_update_token(owner, token, noback, 0); } return; } /*************************************************/ /* browser_show_token() */ /* */ /* Shows a given token at the top of the browser */ /* window. */ /* */ /* If the token is near the bottom of the page, */ /* then the page extent is increased so that the */ /* window may still be scrolled to show the */ /* token at the top. Despite introducing some */ /* dead space at the page base, it gets very */ /* confusing - particularly with Find functions */ /* - to not be able to rely on the token being */ /* actually moved to the top of the window. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the window; */ /* */ /* The token address; */ /* */ /* Offset into the token data which */ /* the chunk representing the token */ /* must straddle - i.e., it must */ /* start at less than or equal to */ /* this offset and end at greater */ /* than it. */ /* */ /* Returns: 1 for success, 0 for failure. */ /*************************************************/ int browser_show_token(browser_data * b, HStream * token, int offset) { WimpGetWindowStateBlock s; token_path * path = NULL; reformat_cell * cell = NULL; int orx, ory; int c, l, topy, htop, fy; int found; int depth; if (!token) return 0; /* Find the range of lines the token spans */ depth = tokenutils_line_range(b, token, &l, NULL, NULL, NULL, &path); /* Find out the x and y offset of the cell the */ /* token lies in. */ tokenutils_token_offset(b, path, &orx, &ory); /* If there are valid entries in the token_path structure, */ /* need to find out what line list browser_update_token_r */ /* should be called on. Otherwise, it's the main line */ /* list. */ cell = tokenutils_find_cell(b->cell, depth, path); if (path) free (path); if (!cell) { cell = b->cell; orx = 0; ory = 0; } /* Can't do anything if there are no lines in the cell */ if (!cell->nlines || !cell->ldata || !cell->cdata) return 0; /* Start by checking the token isn't already at the top of the window */ s.window_handle = b->window_handle; if (wimp_get_window_state(&s)) return 0; if (!controls.swap_bars) htop = toolbars_button_height(b) + toolbars_url_height(b); else htop = toolbars_status_height(b); if (htop) htop += wimpt_dy(); topy = s.yscroll - htop; l = 0; while (l < cell->nlines && cell->ldata[l].y + cell->ldata[l].h + ory > topy) l++; if (l < cell->nlines) { for (c = 0; c < cell->ldata[l].n; c++); { /* If the chunk represents the given token, */ /* its data offset is less than or equal to the offset specified, or */ /* its data offset plus length is greater than the offset specified, */ /* then we've found the token, it's already at the top so just exit */ /* but flag that the routine was successful. */ if ( cell->cdata[cell->ldata[l].chunks + c].t == token && cell->cdata[cell->ldata[l].chunks + c].o <= offset && cell->cdata[cell->ldata[l].chunks + c].l + cell->cdata[cell->ldata[l].chunks + c].o > offset ) return 1; } } /* It wasn't at the top, so need to do a bit more work. */ l = topy = 0; fy = found = 0; /* Loop through all the lines */ while (l < cell->nlines && !found) { /* Set y to the coordinate of the topmost part of this line (hence the '-1') */ topy = cell->ldata[l].y + cell->ldata[l].h + ory - 1; /* Loop through this line's chunks */ for (c = 0; c < cell->ldata[l].n && !found; c++) { /* Again, if the chunk represents the given token, */ /* has an offset below or equal to the given offset, or */ /* either has a data offset plus length greater than the offset specified */ /* or has zero data length, */ /* then flag that we've found the token and get the y coordinate of the */ /* top of this line into fy. */ if ( cell->cdata[cell->ldata[l].chunks + c].t == token && cell->cdata[cell->ldata[l].chunks + c].o <= offset && ( cell->cdata[cell->ldata[l].chunks + c].l + cell->cdata[cell->ldata[l].chunks + c].o > offset || cell->cdata[cell->ldata[l].chunks + c].l == 0 ) ) fy = topy, found = 1; } l++; } /* If found = 0, the loop didn't find the requested token, */ /* so exit, flagging failure. */ if (!found) return 0; /* Jump to the scroll position recorded in fy. */ s.yscroll = fy + htop; /* Is the window extent great enough? */ { BBox extent; int bottom_coord; window_get_extent(0, b->self_id, &extent); bottom_coord = s.yscroll - (s.visible_area.ymax - s.visible_area.ymin); /* If the extent is not great enough, increase it */ if (extent.ymin >= bottom_coord) { extent.ymin = bottom_coord; if (window_set_extent(0, b->self_id, &extent)) return 0; } } if (wimp_open_window((WimpOpenWindowBlock *) &s)) return 0; return 1; } /*************************************************/ /* browser_ensure_visible() */ /* */ /* Ensures that a given token is wholly visible */ /* in the browser window, scrolling down or up */ /* if needed. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the token; */ /* */ /* A WimpGetWindowStateBlock pointer */ /* for the browser window; */ /* */ /* Pointer to the token itself. */ /* */ /* Returns: 1 if the window was scrolled, */ /* else 0. If there is an error */ /* internally, 2 is returned. */ /*************************************************/ int browser_ensure_visible(browser_data * b, WimpGetWindowStateBlock * state, HStream * token) { int lfir, llas, ltop, lbot; int ytop, ybot, htop, hbot; HStream * ttop; HStream * tend; reformat_cell * cell_s = NULL, * cell_e = NULL; token_path * path_s = NULL, * path_e = NULL; int depth_s = 0; int depth_e = 0; int orx_s, ory_s; int orx_e, ory_e; if (!token) return 0; /* Find the first and last lines to include this token range */ tokenutils_anchor_range(b, token, &ttop, &tend); if (!ttop || !tend) depth_s = tokenutils_line_range(b, token, &lfir, NULL, &llas, NULL, &path_s); else { depth_s = tokenutils_line_range(b, ttop, &lfir, NULL, NULL, NULL, &path_s); depth_e = tokenutils_line_range(b, tend, NULL, NULL, &llas, NULL, &path_e); } /* If the first line can't be found, can't proceed */ if (lfir < 0) return 0; /* Find the start/end origin offset */ tokenutils_token_offset(b, path_s, &orx_s, &ory_s); tokenutils_token_offset(b, path_s, &orx_e, &ory_e); /* Find the start/end cell (*should* be the same...) */ cell_s = tokenutils_find_cell(b->cell, depth_s, path_s); cell_e = tokenutils_find_cell(b->cell, depth_e, path_e); if (!cell_s) { cell_s = b->cell; orx_s = 0; ory_s = 0; } if (!cell_e) { cell_e = b->cell; orx_e = 0; ory_e = 0; } /* Free the token_path arrays */ if (path_s) free (path_s); if (path_e) free (path_e); /* If the last line couldn't be found, the token only */ /* spans the one line. */ if (llas < 0) llas = lfir, cell_e = cell_s; /* Work out the top and bottom y coordinates that the token spans */ ltop = ory_s + cell_s->ldata[lfir].y + cell_s->ldata[lfir].h - 1; /* '-1' as we want this to be an inclusive coord */ lbot = ory_e + cell_e->ldata[llas].y; /* Work out where the visible page region starts and ends */ /* (affected by window scrolling and toolbar presence). */ if (!controls.swap_bars) { htop = toolbars_button_height(b) + toolbars_url_height(b); hbot = toolbars_status_height(b); } else { htop = toolbars_status_height(b); hbot = toolbars_button_height(b) + toolbars_url_height(b); } if (htop) htop += wimpt_dy(); if (hbot) hbot += wimpt_dy(); ytop = state->yscroll - htop; ybot = state->yscroll - (state->visible_area.ymax - state->visible_area.ymin) + hbot; /* It would be possible for the token to be big, or the window to be small, */ /* and both the bottom and top of it be off the window. In this case, take */ /* the case where the top is not visible over the case where the bottom is */ /* not visible. */ if (ltop > ytop) { state->yscroll = ltop + htop + 8; /* '+8' = aesthetics */ if (wimp_open_window((WimpOpenWindowBlock *) state)) return 2; else return 1; } else if (lbot < ybot) { state->yscroll = lbot + (state->visible_area.ymax - state->visible_area.ymin) - hbot - 8; /* '-8 = aesthetics */ if (wimp_open_window((WimpOpenWindowBlock *) state)) return 2; else return 1; } return 0; } /*************************************************/ /* browser_check_visible() */ /* */ /* Checks to see if a given token is wholly or */ /* partially visible in a given browser window. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the token; */ /* */ /* A WimpGetWindowStateBlock pointer */ /* for the browser window (may be */ /* NULL); */ /* */ /* Pointer to the token itself. */ /* */ /* Returns: 1 if the token is wholly visible, */ /* 2 if it is partially visible, */ /* else 0. If there is an error */ /* internally, 0 is returned. */ /* */ /* Assumes: If the WimpGetWindowStateBlock */ /* pointer is NULL, the function */ /* will find the information out */ /* itself. The block pointer can be */ /* passed in as calling functions */ /* may well already have this block */ /* available, so it makes sense to */ /* avoid finding it out again. */ /*************************************************/ int browser_check_visible(browser_data * b, WimpGetWindowStateBlock * state, HStream * token) { WimpGetWindowStateBlock s; int lfir, llas, ltop, lbot; int ytop, ybot, htop, hbot; HStream * ttop; HStream * tend; reformat_cell * cell_s = NULL, * cell_e = NULL; token_path * path_s = NULL, * path_e = NULL; int depth_s = 0; int depth_e = 0; int orx_s, ory_s; int orx_e, ory_e; if (!token) return 0; /* Get the window state, if necessary */ if (!state) { s.window_handle = b->window_handle; if (wimp_get_window_state(&s)) return 0; state = &s; } /* Find the first and last lines to include this token range */ tokenutils_anchor_range(b, token, &ttop, &tend); if (!ttop || !tend) depth_s = tokenutils_line_range(b, token, &lfir, NULL, &llas, NULL, &path_s); else { depth_s = tokenutils_line_range(b, ttop, &lfir, NULL, NULL, NULL, &path_s); depth_e = tokenutils_line_range(b, tend, NULL, NULL, &llas, NULL, &path_e); } /* If the first line can't be found, can't proceed */ if (lfir < 0) return 0; /* Find the start/end origin offset */ tokenutils_token_offset(b, path_s, &orx_s, &ory_s); tokenutils_token_offset(b, path_s, &orx_e, &ory_e); /* Find the start/end cell (*should* be the same...) */ cell_s = tokenutils_find_cell(b->cell, depth_s, path_s); cell_e = tokenutils_find_cell(b->cell, depth_e, path_e); if (!cell_s) { cell_s = b->cell; orx_s = 0; ory_s = 0; } if (!cell_e) { cell_e = b->cell; orx_e = 0; ory_e = 0; } /* Free the token_path arrays */ if (path_s) free (path_s); if (path_e) free (path_e); /* If the last line couldn't be found, the token only */ /* spans the one line. */ if (llas < 0) llas = lfir, cell_e = cell_s; /* Work out where the visible page region starts and ends */ /* (affected by window scrolling and toolbar presence). */ if (!controls.swap_bars) { htop = toolbars_button_height(b) + toolbars_url_height(b); hbot = toolbars_status_height(b); } else { htop = toolbars_status_height(b); hbot = toolbars_button_height(b) + toolbars_url_height(b); } if (htop) htop += wimpt_dy(); if (hbot) hbot += wimpt_dy(); ytop = state->yscroll - htop; ybot = state->yscroll - (state->visible_area.ymax - state->visible_area.ymin) + hbot; /* Work out the top and bottom y coordinates that the token spans */ ltop = ory_s + cell_s->ldata[lfir].y + cell_s->ldata[lfir].h - 1; /* '-1' as we want this to be an inclusive coord */ lbot = ory_e + cell_e->ldata[llas].y; /* Is the token fully visible? */ if (ltop <= ytop && lbot >= ybot) return 1; /* Work out the top and bottom to check for partial visibility */ ltop = ory_s + cell_s->ldata[lfir].y; lbot = ory_e + cell_e->ldata[llas].y + cell_e->ldata[llas].h - 1; /* Is the token partially visible? */ if (ltop <= ytop && lbot >= ybot) return 2; /* The token is not in the visible area */ return 0; } /*************************************************/ /* browser_show_named_anchor() */ /* */ /* Given an anchor name, ensures that the window */ /* is scrolled to show the token associated */ /* with that anchor name. If no match can be */ /* found between the given name and the names of */ /* the tokens, the routine gives an appropriate */ /* error message back to the user. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the window; */ /* */ /* Pointer to the anchor name. */ /*************************************************/ void browser_show_named_anchor(browser_data * b, char * anchor) { char name[Limits_NamedAnchor]; memset(name, 0, sizeof(name)); strncpy(name, anchor, sizeof(name) - 2); if ( !browser_show_token(b, fetch_find_anchor_token(b, name), 0) ) { /* Some broken pages specify the names with a hash in front, */ /* and still expect them to work. Try to deal with that. */ memmove(name + 1, name, strlen(name)); name[0] = '#'; if ( !browser_show_token(b, fetch_find_anchor_token(b, name), 0) ) { erb.errnum = Utils_Error_Custom_Message; /* Give a different message if still fetching */ if (fetch_fetching(b)) { StrNCpy0(erb.errmess, lookup_token("NoLabelF:The label '%0' cannot be found, but the page is still fetching - try again when the page fetch has finished.", 0, anchor)) } else { StrNCpy0(erb.errmess, lookup_token("NoLabel:The label '%0' cannot be found on this page.", 0, anchor)) } /* Report the error and continue from here */ show_error_ret(&erb); } } return; } /*************************************************/ /* browser_display_local_reference() */ /* */ /* Checks a given URL against a given base URL, */ /* and if it contains a local reference (i.e. */ /* has '#<name>' at the end) but otherwise */ /* matches the base URL, will try to find and */ /* subsequently display the reference on the */ /* page. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the page; */ /* */ /* Pointer to the requested URL */ /* which may contain the reference; */ /* */ /* Pointer to the base URL against */ /* which the first is compared. */ /* */ /* Returns: 1 if the reference is to be shown */ /* (though if the reference may not */ /* actually be found on the page) or */ /* 0 if the reference will not be */ /* shown, because the base URL does */ /* not match the requested URL. */ /* */ /* Assumes: Either pointer may be NULL, */ /* though this will ensure that 0 is */ /* returned and thus it's not much */ /* use giving null pointers. */ /* The application must be able to */ /* write to the URL data. */ /*************************************************/ int browser_display_local_reference(browser_data * b, char * url_requested, char * url_current) { char * p1, * p2; /* Can't do anything if null pointers are given */ if (!url_requested || !url_current) return 0; /* Can't do anything if the requested URL doesn't have a local reference in it */ p1 = fetch_find_name_tag(url_requested); if (!p1) return 0; /* If the base URL contains a reference then point p2 to the first */ /* character after the main URL (i.e. the '#'), else point p2 to */ /* the end of the string. */ p2 = fetch_find_name_tag(url_current); if (!p2) p2 = strchr(url_current, 0); /* If the two URLs don't match, return 0 */ if ( utils_strncasecmp(url_requested, url_current, (int) p1 - (int) url_requested) ) return 0; /* Otherwise, show the reference */ browser_show_named_anchor(b, p1 + 1); return 1; } /*************************************************/ /* browser_set_look() */ /* */ /* Sets the 'look' of a browser window - i.e. */ /* underlined links, using document or default */ /* colour schemes, etc. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* to alter (any in a frameset will */ /* do, as all frames are updated); */ /* */ /* Object ID of the item generating */ /* this change, if appropriate (or */ /* zero if not); */ /* */ /* 1 to underline links, 0 not to, */ /* -1 to not change this state; */ /* */ /* 1 to use document colours, 0 to */ /* use defaults, -1 to not change */ /* this state; */ /* */ /* 1 to show foreground images, 0 */ /* not to (any pending image fetches */ /* are started up again if 1 is */ /* given), or -1 to not change this */ /* state; */ /* */ /* 1 to show background images, 0 */ /* not to (any pending image fetch */ /* for this is started if 1 is */ /* given), or -1 to not change this */ /* state. */ /*************************************************/ _kernel_oserror * browser_set_look(browser_data * b, ObjectId source, int underline_links, int use_source_cols, int show_foreground, int show_background) { /* Check the browser_data pointer is valid */ if (!b || !is_known_browser(b)) return NULL; /* Find the ancestor and call the recursive back-end with it */ return browser_set_look_r(utils_ancestor(b), source, underline_links, use_source_cols, show_foreground, show_background); } /*************************************************/ /* browser_set_look_r() */ /* */ /* Recursive back-end to browser_set_look. */ /* */ /* Note that if open, the Print Style dialogue */ /* is updated with these changes, if they apply */ /* to the browser to which it is relevant (if */ /* any). */ /* */ /* Parameters: As for browser_set_look. All */ /* children of the browser_data */ /* struct, along with that given */ /* struct, will be updated. */ /*************************************************/ static _kernel_oserror * browser_set_look_r(browser_data * b, ObjectId source, int underline_links, int use_source_cols, int show_foreground, int show_background) { int child; int redraw = 0; /* Scan the child tree */ if (b->nchildren) { for (child = 0; child < b->nchildren; child ++) { RetError(browser_set_look_r(b->children[child], source, underline_links, use_source_cols, show_foreground, show_background)); } } /* Work through the four options, updating the browser if they */ /* seem to have changed. */ if (underline_links >= 0 && underline_links != b->underline_links) { b->underline_links = underline_links; redraw = 1; } if (use_source_cols >= 0 && use_source_cols != b->use_source_cols) { b->use_source_cols = use_source_cols; redraw = 1; } if (show_foreground >= 0 && show_foreground != b->show_foreground) { b->show_foreground = show_foreground; redraw = 1; /* Restart fetches if required */ if (show_foreground) image_restart_fetches(b, 1, 0); } if (show_background >= 0 && show_background != b->show_background) { b->show_background = show_background; /* No need to redraw if there's no background image to show or remove */ redraw = b->background_image >= 0 ? 1 : 0; /* Again restart fetches if required */ if (show_background) image_restart_fetches(b, 0, 1); } /* If required, redraw the browser to reflect the changes */ if (redraw) { WimpGetWindowStateBlock s; s.window_handle = b->window_handle; RetError(wimp_get_window_state(&s)); coords_box_toworkarea(&s.visible_area, (WimpRedrawWindowBlock *) &s); RetError(wimp_force_redraw(b->window_handle, s.visible_area.xmin, s.visible_area.ymin, s.visible_area.xmax, s.visible_area.ymax)); } /* If there's a Print Style dialogue opened for this browser, the */ /* following call will update it appropriately. */ return printstyle_set_look(source, b->self_id, underline_links, use_source_cols, show_foreground, show_background); } /*************************************************/ /* browser_give_general_focus() */ /* */ /* Places the caret in a browser window, but in */ /* no particular icon - it will appear in the */ /* URL bar if one is visible, else the main */ /* window will gain the input focus but the */ /* caret will not be visible. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the window. */ /*************************************************/ _kernel_oserror * browser_give_general_focus(browser_data * b) { _kernel_oserror * e; ObjectId t; if (!b) return NULL; /* Try to put the caret in a form - exit with NULL if it succeeds */ if (form_give_focus(b)) return NULL; /* OK, put the focus in the URL bar, or if not there, in the main */ /* window (the caret will be invisible in this latter case). */ /* */ /* In the special case of the URL bar and status display being */ /* merged, the focus is given to the URL writable only if it is */ /* currently visible in the window. */ t = toolbars_get_upper(b); if ( t && b->url_bar && !gadget_hidden(t, URLBarWrit) ) return gadget_set_focus(0, t, URLBarWrit); else { browser_data * ancestor = utils_ancestor(b); /* Only give input focus to the ancestor */ e = wimp_set_caret_position(ancestor->window_handle, -1, 0, 0, -1, -1); if (e) return e; /* May need to select a new object */ if (!ancestor->selected && choices.keyboard_ctrl) { browser_data * next = frames_find_another_frame(ancestor, 0); if (next) { ancestor->selected = browser_find_first_selectable(next, NULL, 0); if (ancestor->selected) { ancestor->selected_owner = next; ancestor->selected_frame = b; frames_highlight_frame(b); } } } } return NULL; } /*************************************************/ /* browser_inherit() */ /* */ /* Makes a given child browser inherit some of */ /* the characteristics of a given parent. */ /* */ /* The post_data field is handled specially. */ /* Please see browser_inherit_post_data for */ /* details. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* representing the parent; */ /* */ /* Pointer to a browser_data struct */ /* representing the child. */ /*************************************************/ _kernel_oserror * browser_inherit(browser_data * parent, browser_data * child) { /* Be very cautious! */ if (!is_known_browser(parent) || !is_known_browser(child)) return NULL; /* The local History */ RetError(history_inherit(parent, child)); /* Some display flags */ child->underline_links = parent->underline_links; child->show_foreground = parent->show_foreground; child->show_background = parent->show_background; child->use_source_cols = parent->use_source_cols; /* To finish, deal with post_data information */ return browser_inherit_post_data(parent, child); } /*************************************************/ /* browser_inherit_post_data() */ /* */ /* Part of browser_inherit which needs to be */ /* used sometimes for windows that already exist */ /* but are being targetted by a POST form */ /* submission. */ /* */ /* If there is a flex block attached through the */ /* post_data field of the parent, the contents */ /* will be copied into a flex block attached to */ /* the child's post_data field and then *freed* */ /* in the parent. */ /* */ /* This is because at present, the only time the */ /* parent will have such a block is if either an */ /* a button that submits a POST request was */ /* clicked upon, or if that button targets */ /* another browser window. In this case, you */ /* don't want to leave the data attached to the */ /* parent or the next fetch it does will */ /* erroneously be sent as POST itself...! */ /* */ /* NB If the child which is to receive the flex */ /* data from the parent (assuming the parent has */ /* any to give!) already had stuff attached to */ /* post_data, this will obviously be freed */ /* first. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* representing the parent; */ /* */ /* Pointer to a browser_data struct */ /* representing the child. */ /*************************************************/ _kernel_oserror * browser_inherit_post_data(browser_data * parent, browser_data * child) { /* Once more, because we may be called from anywhere, */ /* be very cautious. */ if (!is_known_browser(parent) || !is_known_browser(child)) return NULL; /* If the parent had extra data, we should carry that forward (this */ /* may have been an adjust-click on a Submit button for a POST form */ /* for example). */ if (parent->post_data) { int success; int size = flex_size(&parent->post_data); if (size) { /* Clear any data in the child */ if (child->post_data) flex_free(&child->post_data); /* Allocate an appropriate chunk of memory in the child */ success = flex_alloc(&child->post_data, size); /* If the allocation succeeded, so switch off flex budging and */ /* copy the block contents to the child browser */ if (success) { int oldstate = flex_set_budge(0); memcpy(child->post_data, parent->post_data, size); /* Restore flex's budge state */ flex_set_budge(oldstate); } /* At this point, whether the new flex allocation worked */ /* or not, free the parent's block (see the comments at */ /* the top of the function for more details). */ flex_free(&parent->post_data); /* If the flex allocation failed, return an error */ if (!success) return make_no_fetch_memory_error(16); } } return NULL; }