/* 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 : CSIM.c */ /* */ /* Purpose: Client-side image map support. */ /* */ /* Author : A.D.Hodgkinson */ /* */ /* History: 13-Nov-97: Created. */ /***************************************************/ #include <stdlib.h> #include <stdio.h> #include <string.h> #include "swis.h" #include "HTMLLib.h" /* HTML library API, Which will include html2_ext.h, tags.h and struct.h */ #include "wimp.h" #include "wimplib.h" #include "event.h" #include "svcprint.h" #include "Global.h" #include "FromROSLib.h" #include "MiscDefs.h" #include "Utils.h" #include "Fetch.h" #include "Redraw.h" #include "CSIM.h" /* Local structures */ typedef struct csim_polygon_vertex { int x; int y; } csim_polygon_vertex; /*************************************************/ /* csim_within() */ /* */ /* Given a token representing an A tag with */ /* associated AREA-like information and x and y */ /* coordinates in the same coordinate space as */ /* the A tag, works out if those coordinates lie */ /* within the area the A tag describes. */ /* */ /* Parameters: Pointer to the token; */ /* */ /* X coordinate; */ /* */ /* Y coordinate. */ /* */ /* Returns: 1 if the coordinates lie within */ /* the area described by the token, */ /* 2 if they lie within it because */ /* the item is the DEFAULT area, and */ /* 0 if the coordinates do not lie */ /* within the area. */ /* */ /* Assumes: The token represents an anchor */ /* with associated coordinate info. */ /*************************************************/ int csim_within(HStream * t, int relx, int rely) { areashape type; int n; if (!t->coords) return 0; /* Find the shape type and number of coordinates */ type = (areashape) t->coords[0]; n = t->coords[1]; /* Deal with each type */ switch (type) { /* Rectangles must have four coordinates */ case areashape_RECT: { if (n != 4) return 0; return ( relx >= t->coords[2] && /* xmin */ rely >= t->coords[3] && /* ymin */ relx <= t->coords[4] && /* xmax */ rely <= t->coords[5] /* ymax */ ); } break; /* Circles need 3 coordinates (x, y, r) */ case areashape_CIRCLE: { if (n != 3) return 0; relx -= t->coords[2]; rely -= t->coords[3]; return ( relx * relx + rely * rely < t->coords[4] * t->coords[4] ); } break; /* A polygon needs at least 3 coordinate pairs, and we must */ /* have an even number of them. */ case areashape_POLY: { /* Check we've the right number of coordinates */ if (n < 6 || n % 2) return 0; else { /* Adapted from clientmaps.c (S.Brodie), which was in */ /* turn from imagemap.c, (Rob McCool). */ int crossings = 0; int xflag0; int y; const csim_polygon_vertex * const v0 = (csim_polygon_vertex *) (&(t->coords[2])); const csim_polygon_vertex * const vlast = (csim_polygon_vertex *) (&(t->coords[n])); /* (Since coord 1 starts at array index 2) */ const int * stop = ((int *) v0) + n; const int * p; y = vlast->y; p = &v0->y; /* Does the given y coordinate lie between the first and last vertex y positions? */ if ((y >= rely) != (*p >= rely)) { /* Set xflag0 if the given x coordinate is left of the last vertex x position */ xflag0 = vlast->x >= relx; /* Compare whether this against whether or not the given x coordinate is left */ /* of the first vertex x position. */ if (xflag0 == (v0->x >= relx)) { /* If xflag0 is set here, the given x coordinate is left of both the first */ /* and last vertex x positions - increment 'crossings'. Else it is right */ /* of them both. */ if (xflag0) ++crossings; } else { /* If we reach here, the given x coordinate is neither wholly to the left */ /* of the first and last vertex x positions, or to the right; so it must */ /* lie between them. */ crossings += ( vlast->x - ( ((y - rely) * (v0->x - vlast->x)) / (*p - y) ) ) >= relx; } } /* Start with y pointing to the first vertex y coordinate, */ /* then increment p to point to the next y coordinate. */ /* We can thus go round the polygon comparing coordinate */ /* pairs (sides of the polygon) against the given coords. */ for (y = *p, p += 2; p < stop; y = *p, p += 2) { if (y >= rely) { /* If the given y coordinate lies left of the current vertex */ /* y coordinate, skip 'p' along until it points to another */ /* y coordinate right of the given one. */ while (p < stop && (*p >= rely)) p += 2; } else { /* ...Or if the given y coordinate lies to the right of the */ /* current vertex, skip 'p' long until it points to a y */ /* coordinate right of the given one. */ while (p < stop && (*p < rely)) p += 2; } /* If p is no longer in the vertex array, then there is no vertex */ /* to the right of the given y coordinate and the given point */ /* cannot lie in the polygon. */ if (p >= stop) break; /* So 'p' points to the second y coordinate of a pair of vertices; */ /* 'p - 3' thus points to the first x coordinate, and so-on. So, */ /* set xflag0 if the first vertex x coordinate is right of the */ /* given x coordinate. */ xflag0 = (*(p - 3) >= relx); /* Compare this against whether or not the second vertex x coordinate */ /* is in the same position relative to (i.e. left or right of) the */ /* given x coordinate. */ if (xflag0 == (*(p - 1) >= relx)) { /* If xflag0 is set, then, then the given x coordinate lies to the left of */ /* both of the vertices (increment crossings). Else it's to the right. */ if (xflag0) ++crossings; } else { /* If we reach here, the given x coordinate is between the two vertices' */ /* x coordinates. */ crossings += ( *(p - 3) - ( ((*(p - 2) - rely) * (*(p - 1) - *(p - 3))) / (*p - *(p - 2)) ) ) >= relx; } } return (crossings & 1); } } break; /* For DEFAULT areas, the coordinates always lie in it... */ case areashape_DEFAULT: { return 2; } break; } return 0; } /*************************************************/ /* csim_return_info */ /* */ /* The high level interface. Given a browser, */ /* token and relative coordinates from the top */ /* left of the image the token represents, */ /* returns a URL and target - the token must */ /* represent an Object, which is an image with */ /* image map details attached. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the map; */ /* */ /* Pointer to an HStream struct */ /* representing an Object, which is */ /* an image with an attached client */ /* side map; */ /* */ /* X offset from the top left of the */ /* image in pixels to look up in the */ /* map; */ /* */ /* Similarly, Y offset (positive */ /* downwards); */ /* */ /* Pointer to a char *, in which a */ /* pointer to the URL to fetch will */ /* be placed; */ /* */ /* Pointer to a char *, in which a */ /* pointer to a target window name */ /* for the fetch will be placed; */ /* */ /* Pointer to a char *, in which a */ /* pointer to alternative text */ /* to display in place of the */ /* URL will be placed. */ /* */ /* Returns: See parameters; note that any of */ /* the values returned may be NULL. */ /* If the URL is NULL, then the */ /* token didn't represent an image */ /* map or the coordinates didn't lie */ /* within a region the map defines. */ /* */ /* Assumes: Any of the char * pointers may be */ /* NULL. */ /*************************************************/ void csim_return_info(browser_data * b, HStream * t, int relx, int rely, char ** url, char ** target, char ** alt) { HStream * item; char * defurl = NULL; char * deftarget = NULL; char * defalt = NULL; /* Start with everything pointing nowhere */ if (url) *url = NULL; if (target) *target = NULL; if (alt) *alt = NULL; /* Initial sanity checks */ if (!b || !t) return; if (!(t->type & TYPE_ISCLIENTMAP)) return; /* Now start looking at what area the coordinates are over */ if (ISOBJECT(t) & HtmlOBJECTshapes(t)) item = HtmlOBJECTstream(t); else item = HtmlOBJECTmapstream(t); if (!item) return; /* Go through the Object's token stream */ while (item) { /* Is this an A tag with associated URL? */ if (ISLINK(item)) { int result = csim_within(item, relx, rely); if (result == 1) { /* Found it (remember, if areas overlap, we */ /* are supposed to take the first). */ if (url) *url = item->anchor; if (target) *target = item->target; if (alt) *alt = item->text; return; } if (result == 2) { /* Found the default details, to use if all else fails */ defurl = item->anchor; deftarget = item->target; defalt = item->text; } } /* Try the next HStream structure */ item = item->next; } /* Finished - return whatever defaults were specified in */ /* the map, or NULL if there were none. */ if (url) *url = defurl; if (target) *target = deftarget; if (alt) *alt = defalt; return; } /*************************************************/ /* csim_highlight_region() */ /* */ /* Draw a border around whatever region the */ /* map_x and map_y fields of a browser_data */ /* structure indicate. */ /* */ /* Parameters: Pointer to the browser_data */ /* struct holding info on the token */ /* representing the map and the */ /* offset x and y coordinates */ /* indicating what part of the map */ /* is to be highlighted; */ /* */ /* Colour to use for the region */ /* highlight (see redraw_set_colour */ /* in Redraw.c for the format of */ /* the colour number); */ /* */ /* Left hand x coordinate (screen */ /* coords) that the image is plotted */ /* at; */ /* */ /* Similarly the top y coordinate */ /* that the image is plotted at. */ /*************************************************/ void csim_highlight_region(browser_data * b, int colour, int left_x, int top_y) { HStream * t; HStream * item; /* Initial sanity checks */ if (!b) return; t = b->pointer_over; if (!t || !(t->type & TYPE_ISCLIENTMAP)) return; /* Now start looking at what area the coordinates are over */ if (ISOBJECT(t) & HtmlOBJECTshapes(t)) item = HtmlOBJECTstream(t); else item = HtmlOBJECTmapstream(t); if (!item) return; /* Go through the Object's token stream */ while (item) { /* Is this an A tag with associated URL? */ if (ISLINK(item)) { if (csim_within(item, b->map_x, b->map_y)) { /* Found it (remember, if areas overlap, we */ /* are supposed to take the first). */ areashape type; int n; if (!item->coords) return; /* Find the shape type and number of coordinates */ type = (areashape) item->coords[0]; n = item->coords[1]; /* Deal with each type */ switch (type) { case areashape_RECT: { BBox rect; if (n != 4) return; rect.xmin = left_x + item->coords[2] * 2; /* 1 'web pixel' = 2 OS units */ rect.ymin = top_y - item->coords[5] * 2; rect.xmax = left_x + item->coords[4] * 2; rect.ymax = top_y - item->coords[3] * 2; redraw_border_around_box(&rect, colour); } break; case areashape_CIRCLE: { if (n != 3) return; redraw_set_colour(colour); /* Draw the main circle around the outside */ /* of the area, then 'blur' around a 1 OS unit */ /* centre point to get a thicker outline. */ bbc_circle(left_x + item->coords[2] * 2, /* 1 'web pixel' = 2 OS units */ top_y - item->coords[3] * 2, item->coords[4] * 2 + 2); bbc_circle(left_x + item->coords[2] * 2 + 1, top_y - item->coords[3] * 2 + 1, item->coords[4] * 2 + 2); bbc_circle(left_x + item->coords[2] * 2 + 1, top_y - item->coords[3] * 2 - 1, item->coords[4] * 2 + 2); bbc_circle(left_x + item->coords[2] * 2 - 1, top_y - item->coords[3] * 2 + 1, item->coords[4] * 2 + 2); bbc_circle(left_x + item->coords[2] * 2 - 1, top_y - item->coords[3] * 2 - 1, item->coords[4] * 2 + 2); } break; case areashape_POLY: { int coord; int vx, vy; int lx, ly; if (n < 6 || n % 2) return; redraw_set_colour(colour); lx = left_x + item->coords[2] * 2; /* 1 'web pixel' = 2 OS units */ ly = top_y - item->coords[3] * 2; for ( coord = 2, vx = left_x + item->coords[coord + 2] * 2, vy = top_y - item->coords[coord + 3] * 2; coord < n; coord += 2, vx = left_x + item->coords[coord + 2] * 2, vy = top_y - item->coords[coord + 3] * 2 ) { /* Draw the outline at the exact border coordinates */ /* and 'blur' 1 OS unit in NE/NW/SE/SW directions to */ /* make the border thicker. */ bbc_move(lx, ly); bbc_draw(vx, vy); bbc_move(lx + 1, ly + 1); bbc_draw(vx + 1, vy + 1); bbc_move(lx + 1, ly - 1); bbc_draw(vx + 1, vy - 1); bbc_move(lx - 1, ly + 1); bbc_draw(vx - 1, vy + 1); bbc_move(lx - 1, ly - 1); bbc_draw(vx - 1, vy - 1); lx = vx, ly = vy; } } break; } /* Finished */ return; } } /* Try the next HStream structure */ item = item->next; } /* Finished */ return; }