home *** CD-ROM | disk | FTP | other *** search
- /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- *
- * The contents of this file are subject to the Netscape Public License
- * Version 1.0 (the "NPL"); you may not use this file except in
- * compliance with the NPL. You may obtain a copy of the NPL at
- * http://www.mozilla.org/NPL/
- *
- * Software distributed under the NPL is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
- * for the specific language governing rights and limitations under the
- * NPL.
- *
- * The Initial Developer of this code under the NPL is Netscape
- * Communications Corporation. Portions created by Netscape are
- * Copyright (C) 1998 Netscape Communications Corporation. All Rights
- * Reserved.
- */
-
- #include "xp.h"
- #include "pa_parse.h"
- #include "layout.h"
- #include "laylayer.h"
-
-
- extern Bool lo_is_location_in_poly(int32 x, int32 y,
- int32 *coords, int32 coord_cnt);
-
- /* #ifndef NO_TAB_NAVIGATION */
- extern Bool lo_find_location_in_poly(int32 *xx, int32 *yy,
- int32 *coords, int32 coord_cnt);
- /* NO_TAB_NAVIGATION */
-
-
- /*
- * This function parses a list of numbers (coordinates).
- * The numbers can have any integer value, and it is a comma separated list.
- * (optionally, whitespace can replace commas as separators in the list)
- */
- int32 *
- lo_parse_coord_list(char *str, int32 *value_cnt, Bool must_be_odd)
- {
- char *tptr;
- char *n_str;
- int32 i, cnt, acnt;
- int32 *value_list;
-
- /*
- * Nothing in an empty list
- */
- *value_cnt = 0;
- if ((str == NULL)||(*str == '\0'))
- {
- return((int32 *)NULL);
- }
-
- /*
- * Skip beginning whitespace, all whitespace is empty list.
- */
- n_str = str;
- while (XP_IS_SPACE(*n_str))
- {
- n_str++;
- }
- if (*n_str == '\0')
- {
- return((int32 *)NULL);
- }
-
- /*
- * Make a pass where any two numbers separated by just whitespace
- * are given a comma separator. Count entries while passing.
- */
- cnt = 0;
- while (*n_str != '\0')
- {
- Bool has_comma;
-
- /*
- * Skip to a separator
- */
- tptr = n_str;
- while ((!XP_IS_SPACE(*tptr))&&(*tptr != ',')&&(*tptr != '\0'))
- {
- tptr++;
- }
- n_str = tptr;
-
- /*
- * If no more entries, break out here
- */
- if (*n_str == '\0')
- {
- break;
- }
-
- /*
- * Skip to the end of the separator, noting if we have a
- * comma.
- */
- has_comma = FALSE;
- while ((XP_IS_SPACE(*tptr))||(*tptr == ','))
- {
- if (*tptr == ',')
- {
- if (has_comma == FALSE)
- {
- has_comma = TRUE;
- }
- else
- {
- break;
- }
- }
- tptr++;
- }
- /*
- * If this was trailing whitespace we skipped, we are done.
- */
- if ((*tptr == '\0')&&(has_comma == FALSE))
- {
- break;
- }
- /*
- * Else if the separator is all whitespace, and this is not the
- * end of the string, add a comma to the separator.
- */
- else if (has_comma == FALSE)
- {
- *n_str = ',';
- }
-
- /*
- * count the entry skipped.
- */
- cnt++;
-
- n_str = tptr;
- }
- /*
- * count the last entry in the list.
- */
- cnt++;
-
- /*
- * For polygons, we need a fake empty coord at the
- * end of the list of x,y pairs.
- */
- if ((must_be_odd != FALSE)&&((cnt & 0x01) == 0))
- {
- acnt = cnt + 1;
- }
- else
- {
- acnt = cnt;
- }
-
- *value_cnt = acnt;
-
- /*
- * Allocate space for the coordinate array.
- */
- value_list = (int32 *)XP_ALLOC(acnt * sizeof(int32));
- if (value_list == NULL)
- {
- return((int32 *)NULL);
- }
-
- /*
- * Second pass to copy integer values into list.
- */
- tptr = str;
- for (i=0; i<cnt; i++)
- {
- char *ptr;
-
- ptr = strchr(tptr, ',');
- if (ptr != NULL)
- {
- *ptr = '\0';
- }
- /*
- * Strip whitespace in front of number because I don't
- * trust atoi to do it on all platforms.
- */
- while (XP_IS_SPACE(*tptr))
- {
- tptr++;
- }
- if (*tptr == '\0')
- {
- value_list[i] = 0;
- }
- else
- {
- value_list[i] = (int32)XP_ATOI(tptr);
- }
- if (ptr != NULL)
- {
- *ptr = ',';
- tptr = (char *)(ptr + 1);
- }
- }
- return(value_list);
- }
-
-
- /*
- * The beginning of a usemap MAP record.
- * Allocate the structure and initialize it. It will be filled
- * by later AREA tags.
- */
- void
- lo_BeginMap(MWContext *context, lo_DocState *state, PA_Tag *tag)
- {
- PA_Block buff;
- char *str;
- lo_MapRec *map;
-
- map = XP_NEW(lo_MapRec);
- if (map == NULL)
- {
- state->top_state->out_of_memory = TRUE;
- return;
- }
-
- map->name = NULL;
- map->areas = NULL;
- map->areas_last = NULL;
- map->next = NULL;
-
- buff = lo_FetchParamValue(context, tag, PARAM_NAME);
- if (buff != NULL)
- {
- char *name;
-
- PA_LOCK(str, char *, buff);
- if (str != NULL)
- {
- int32 len;
-
- len = lo_StripTextWhitespace(str, XP_STRLEN(str));
- }
- name = (char *)XP_ALLOC(XP_STRLEN(str) + 1);
- if (name == NULL)
- {
- map->name = NULL;
- }
- else
- {
- XP_STRCPY(name, str);
- map->name = name;
- }
- PA_UNLOCK(buff);
- PA_FREE(buff);
- }
- else
- {
- map->name = NULL;
- }
-
- if (map->name == NULL)
- {
- XP_DELETE(map);
- return;
- }
-
- state->top_state->current_map = map;
- }
-
-
- /*
- * Look through the list of finished maps to find one with
- * a matching name. If found, remove it.
- */
- static lo_MapRec *
- lo_remove_named_map(MWContext *context, lo_DocState *state, char *map_name)
- {
- lo_MapRec *prev_map;
- lo_MapRec *map_list;
-
- if (map_name == NULL)
- {
- return(NULL);
- }
-
- prev_map = NULL;
- map_list = state->top_state->map_list;
- while (map_list != NULL)
- {
- /*
- * Found a map with a matching name, return it.
- */
- if (XP_STRCMP(map_name, map_list->name) == 0)
- {
- break;
- }
- prev_map = map_list;
- map_list = map_list->next;
- }
-
- /*
- * No map by that name found.
- */
- if (map_list == NULL)
- {
- return(NULL);
- }
-
- /*
- * If no prev_map, our match is the head of the map_list.
- */
- if (prev_map == NULL)
- {
- state->top_state->map_list = map_list->next;
- }
- else
- {
- prev_map->next = map_list->next;
- }
-
- map_list->next = NULL;
- return(map_list);
- }
-
-
- /*
- * This map is done, add it to the map list.
- */
- void
- lo_EndMap(MWContext *context, lo_DocState *state, lo_MapRec *map)
- {
- lo_MapRec *old_map;
-
- if (map == NULL)
- {
- return;
- }
-
- old_map = lo_remove_named_map(context, state, map->name);
- if (old_map != NULL)
- {
- (void)lo_FreeMap(old_map);
- }
-
- map->next = state->top_state->map_list;
- state->top_state->map_list = map;
- }
-
-
- lo_MapRec *
- lo_FreeMap(lo_MapRec *map)
- {
- lo_MapRec *next;
-
- if (map->areas != NULL)
- {
- lo_MapAreaRec *tmp_area;
- lo_MapAreaRec *areas;
-
- areas = map->areas;
- while (areas != NULL)
- {
- tmp_area = areas;
- areas = areas->next;
- if (tmp_area->alt != NULL)
- {
- PA_FREE(tmp_area->alt);
- }
- if (tmp_area->coords != NULL)
- {
- XP_FREE(tmp_area->coords);
- }
- XP_DELETE(tmp_area);
- }
- }
- if (map->name != NULL)
- {
- XP_FREE(map->name);
- }
- next = map->next;
- XP_DELETE(map);
- return next;
- }
-
-
- /*
- * This is an AREA tag. Create the structure, fill it in based on the
- * attributes passed, and add it to the map record for the current
- * MAP tag.
- */
- void
- lo_BeginMapArea(MWContext *context, lo_DocState *state, PA_Tag *tag)
- {
- PA_Block buff;
- char *str;
- lo_MapRec *map;
- lo_MapAreaRec *area;
- lo_DocLists *doc_lists;
-
- doc_lists = lo_GetCurrentDocLists(state);
-
- /*
- * Get the current map, if there is none, error out.
- */
- map = state->top_state->current_map;
- if (map == NULL)
- {
- return;
- }
-
- area = XP_NEW(lo_MapAreaRec);
- if (area == NULL)
- {
- state->top_state->out_of_memory = TRUE;
- return;
- }
-
- area->type = AREA_SHAPE_RECT;
- area->coords = NULL;
- area->coord_cnt = 0;
- area->anchor = NULL;
- area->alt = NULL;
- area->alt_len = 0;
- area->next = NULL;
-
- buff = lo_FetchParamValue(context, tag, PARAM_SHAPE);
- if (buff != NULL)
- {
- PA_LOCK(str, char *, buff);
- if (pa_TagEqual(S_AREA_SHAPE_RECT, str))
- {
- area->type = AREA_SHAPE_RECT;
- }
- else if (pa_TagEqual(S_AREA_SHAPE_CIRCLE, str))
- {
- area->type = AREA_SHAPE_CIRCLE;
- }
- else if (pa_TagEqual(S_AREA_SHAPE_POLY, str))
- {
- area->type = AREA_SHAPE_POLY;
- }
- else if (pa_TagEqual(S_AREA_SHAPE_POLYGON, str))
- {
- area->type = AREA_SHAPE_POLY;
- }
- else if (pa_TagEqual(S_AREA_SHAPE_DEFAULT, str))
- {
- area->type = AREA_SHAPE_DEFAULT;
- }
- else
- {
- area->type = AREA_SHAPE_UNKNOWN;
- }
- PA_UNLOCK(buff);
- PA_FREE(buff);
- }
-
- /*
- * Get the alt parameter, and store the resulting
- * text, and its length.
- */
- buff = lo_FetchParamValue(context, tag, PARAM_ALT);
- if (buff != NULL)
- {
- PA_LOCK(str, char *, buff);
- area->alt_len = XP_STRLEN(str);
- area->alt_len = (int16)lo_StripTextNewlines(str,
- (int32)area->alt_len);
- PA_UNLOCK(buff);
- }
- area->alt = buff;
-
- /*
- * Parse the comma separated coordinate list into an
- * array of integers.
- */
- buff = lo_FetchParamValue(context, tag, PARAM_COORDS);
- if (buff != NULL)
- {
- int32 cnt;
- Bool must_be_odd;
-
- must_be_odd = FALSE;
- if (area->type == AREA_SHAPE_POLY)
- {
- must_be_odd = TRUE;
- }
- PA_LOCK(str, char *, buff);
- area->coords = lo_parse_coord_list(str, &cnt, must_be_odd);
- if (area->coords != NULL)
- {
- area->coord_cnt = cnt;
- }
- PA_UNLOCK(buff);
- PA_FREE(buff);
- }
-
- /*
- * Get the HREF, and if one exists, get the TARGET to go along
- * with it.
- */
- buff = lo_FetchParamValue(context, tag, PARAM_HREF);
- if (buff != NULL)
- {
- char *target;
- PA_Block targ_buff;
- PA_Block href_buff;
- LO_AnchorData *anchor;
-
- anchor = NULL;
- PA_LOCK(str, char *, buff);
- if (str != NULL)
- {
- int32 len;
-
- len = lo_StripTextWhitespace(str, XP_STRLEN(str));
- }
- str = NET_MakeAbsoluteURL(state->top_state->base_url, str);
- if (str == NULL)
- {
- href_buff = NULL;
- }
- else
- {
- href_buff = PA_ALLOC(XP_STRLEN(str) + 1);
- if (href_buff != NULL)
- {
- char *full_url;
-
- PA_LOCK(full_url, char *, href_buff);
- XP_STRCPY(full_url, str);
- PA_UNLOCK(href_buff);
- }
- else
- {
- state->top_state->out_of_memory = TRUE;
- }
- XP_FREE(str);
- }
- PA_UNLOCK(buff);
- PA_FREE(buff);
-
- if (href_buff != NULL)
- {
- targ_buff = lo_FetchParamValue(context, tag, PARAM_TARGET);
- if (targ_buff != NULL)
- {
- int32 len;
-
- PA_LOCK(target, char *, targ_buff);
- len = lo_StripTextWhitespace(target,
- XP_STRLEN(target));
- if ((*target == '\0')||
- (lo_IsValidTarget(target) == FALSE))
- {
- PA_UNLOCK(targ_buff);
- PA_FREE(targ_buff);
- targ_buff = NULL;
- }
- else
- {
- PA_UNLOCK(targ_buff);
- }
- }
-
- /*
- * If there was no target use the default one.
- * (default provided by BASE tag)
- */
- if ((targ_buff == NULL)&&
- (state->top_state->base_target != NULL))
- {
- targ_buff = PA_ALLOC(XP_STRLEN(
- state->top_state->base_target) + 1);
- if (targ_buff != NULL)
- {
- char *targ;
-
- PA_LOCK(targ, char *, targ_buff);
- XP_STRCPY(targ,
- state->top_state->base_target);
- PA_UNLOCK(targ_buff);
- }
- else
- {
- state->top_state->out_of_memory = TRUE;
- }
- }
-
- anchor = lo_NewAnchor(state, href_buff, targ_buff);
- if (anchor == NULL)
- {
- PA_FREE(href_buff);
- if (targ_buff != NULL)
- {
- PA_FREE(targ_buff);
- }
- }
- /*
- * If the AREA tag has an ALT attribute,
- * stick that text into the anchor data.
- */
- else if (area->alt != NULL)
- {
- PA_Block alt_buff;
- char *alt_text;
-
- PA_LOCK(alt_text, char *, area->alt);
- alt_buff = PA_ALLOC(area->alt_len + 1);
- if (alt_buff != NULL)
- {
- char *new_alt;
-
- PA_LOCK(new_alt, char *, alt_buff);
- XP_STRCPY(new_alt, alt_text);
- PA_UNLOCK(alt_buff);
- }
- PA_UNLOCK(area->alt);
- anchor->alt = alt_buff;
- }
- }
-
- /* If SUPPRESS attribute is present, suppress visual feedback (dashed rectangle)
- when link is selected */
- buff = lo_FetchParamValue(context, tag, PARAM_SUPPRESS);
- if (buff && !XP_STRCASECMP((char*)buff, "true"))
- {
- anchor->flags |= ANCHOR_SUPPRESS_FEEDBACK;
- }
-
- /*
- * Add this url's block to the list
- * of all allocated urls so we can free
- * it later.
- */
- lo_AddToUrlList(context, state, anchor);
- if (state->top_state->out_of_memory != FALSE)
- {
- return;
- }
- area->anchor = anchor;
-
- /*
- * If there are event handlers, reflect this
- * tag into Mocha as a link.
- */
- lo_ReflectLink(context, state, tag, anchor,
- lo_CurrentLayerId(state),
- doc_lists->url_list_len - 1);
- }
-
- /*
- * Add this are to the end of the area list in this
- * map record.
- */
- if (map->areas_last == NULL)
- {
- map->areas = area;
- map->areas_last = area;
- }
- else
- {
- map->areas_last->next = area;
- map->areas_last = area;
- }
- }
-
-
- /*
- * Look through the list of finished maps to find one with
- * a matching name.
- */
- lo_MapRec *
- lo_FindNamedMap(MWContext *context, lo_DocState *state, char *map_name)
- {
- lo_MapRec *map_list;
-
- if (map_name == NULL)
- {
- return(NULL);
- }
-
- map_list = state->top_state->map_list;
- while (map_list != NULL)
- {
- /*
- * Found a map with a matching name, return it.
- */
- if (XP_STRCMP(map_name, map_list->name) == 0)
- {
- break;
- }
- map_list = map_list->next;
- }
- return(map_list);
- }
-
-
- /*
- * Check if a point is in a rectable specified by upper-left and
- * lower-right corners.
- */
- static Bool
- lo_is_location_in_rect(int32 x, int32 y, int32 *coords)
- {
- int32 x1, y1, x2, y2;
-
- x1 = coords[0];
- y1 = coords[1];
- x2 = coords[2];
- y2 = coords[3];
- if ((x1 > x2)||(y1 > y2))
- {
- return(FALSE);
- }
- if ((x >= x1)&&(x <= x2)&&(y >= y1)&&(y <= y2))
- {
- return(TRUE);
- }
- else
- {
- return(FALSE);
- }
- }
-
-
- /*
- * Check if a point is within the radius of a circle specified
- * by center and radius.
- */
- static Bool
- lo_is_location_in_circle(int32 x, int32 y, int32 *coords)
- {
- int32 x1, y1, radius;
- int32 dx, dy, dist;
-
- x1 = coords[0];
- y1 = coords[1];
- radius = coords[2];
- if (radius < 0)
- {
- return(FALSE);
- }
-
- dx = x1 - x;
- dy = y1 - y;
-
- dist = (dx * dx) + (dy * dy);
-
- if (dist <= (radius * radius))
- {
- return(TRUE);
- }
- else
- {
- return(FALSE);
- }
- }
-
-
- /*
- * Check if the passed point is withing the area described in
- * the area structure.
- */
- static Bool
- lo_is_location_in_area(lo_MapAreaRec *area, int32 x, int32 y)
- {
- Bool ret_val;
-
- if (area == NULL)
- {
- return(FALSE);
- }
-
- ret_val = FALSE;
- switch(area->type)
- {
- case AREA_SHAPE_RECT:
- if (area->coord_cnt < 4)
- {
- ret_val = FALSE;
- }
- else
- {
- ret_val = lo_is_location_in_rect(x, y,
- area->coords);
- }
- break;
-
- case AREA_SHAPE_CIRCLE:
- if (area->coord_cnt < 3)
- {
- ret_val = FALSE;
- }
- else
- {
- ret_val = lo_is_location_in_circle(x, y,
- area->coords);
- }
- break;
-
- case AREA_SHAPE_POLY:
- if (area->coord_cnt < 6)
- {
- ret_val = FALSE;
- }
- else
- {
- ret_val = lo_is_location_in_poly(x, y,
- area->coords, area->coord_cnt);
- }
- break;
-
- case AREA_SHAPE_DEFAULT:
- ret_val = TRUE;
- break;
-
- case AREA_SHAPE_UNKNOWN:
- default:
- break;
- }
- return(ret_val);
- }
-
- /*
- * Given an x,y location and a map, return the anchor data for the
- * usemap. Return NULL for areas not described or with no specified href.
- */
- LO_AnchorData *
- lo_MapXYToAnchorData(MWContext *context, lo_DocState *state,
- lo_MapRec *map, int32 x, int32 y)
- {
- lo_MapAreaRec *areas;
-
- if (map == NULL)
- {
- return(NULL);
- }
-
- areas = map->areas;
- while (areas != NULL)
- {
- /*
- * If we found a containing area, return its anchor data.
- */
- if (lo_is_location_in_area(areas, x, y) != FALSE)
- {
- break;
- }
- areas = areas->next;
- }
- if (areas != NULL)
- {
- return(areas->anchor);
- }
- else
- {
- return(NULL);
- }
- }
-
-
- /*
- * Public function for front ends to get the usemap anchor data
- * for an image, based on the x,y of the user in the image.
- */
- LO_AnchorData *
- LO_MapXYToAreaAnchor(MWContext *context, LO_ImageStruct *image,
- int32 x, int32 y)
- {
- int32 doc_id;
- lo_TopState *top_state;
- lo_DocState *state;
- LO_AnchorData *anchor;
- lo_MapRec *map;
-
- /*
- * Get the unique document ID, and retreive this
- * documents layout state.
- */
- doc_id = XP_DOCID(context);
- top_state = lo_FetchTopState(doc_id);
- if ((top_state == NULL)||(top_state->doc_state == NULL))
- {
- return(NULL);
- }
- state = top_state->doc_state;
-
- /*
- * If this is not really a USEMAP image, leave now.
- */
- if (image->image_attr->usemap_name == NULL)
- {
- return(NULL);
- }
-
- /*
- * If we havn't looked up and stored the map
- * pointer for this image before, do that now,
- * otherwise use the stored map pointer.
- */
- if (image->image_attr->usemap_ptr == NULL)
- {
- map = lo_FindNamedMap(context, state,
- image->image_attr->usemap_name);
- image->image_attr->usemap_ptr = (void *)map;
- }
- else
- {
- map = (lo_MapRec *)image->image_attr->usemap_ptr;
- }
-
- anchor = lo_MapXYToAnchorData(context, state, map, x, y);
- return(anchor);
- }
-
- /* #ifndef NO_TAB_NAVIGATION */
- /*
- see LO_MapXYToAreaAnchor() above for accessing the map structure.
- if currentAreaIndex <= 0 , return the first area.
- */
- lo_MapAreaRec *
- LO_getTabableMapArea(MWContext *context, LO_ImageStruct *image, int32 wantedIndex )
- {
- lo_MapRec *map;
- lo_MapAreaRec *areas;
- LO_AnchorData *tempData;
-
-
- if( image == NULL || image->type != LO_IMAGE )
- return( NULL );
-
- /* We don't need the Anchor, but make sure we look up and store the map pointer.*/
- tempData = LO_MapXYToAreaAnchor( context, image, 0, 0);
-
- if (image->image_attr->usemap_name == NULL) /* not really a USEMAP image. */
- return(NULL);
-
- map = (lo_MapRec *)image->image_attr->usemap_ptr;
- if (map == NULL)
- return(NULL);
-
- /* TODO if wantedIndex == -2, return the last tabable area */
- if( wantedIndex <= 0 )
- wantedIndex = 1;
-
- areas = map->areas;
- while (areas != NULL )
- {
- if( areas->anchor != NULL ) { /* only tabable areas are counted */
- if( wantedIndex == 1 )
- return( areas ); /* got it */
- else
- wantedIndex--;
- }
- areas = areas->next;
- }
-
- return( NULL );
- }
-
- Bool
- LO_findApointInArea( lo_MapAreaRec *pArea, int32 *xx, int32 *yy )
- {
-
- if ( pArea == NULL)
- return(FALSE);
-
- switch( pArea->type )
- {
- case AREA_SHAPE_RECT:
- if (pArea->coord_cnt >= 4)
- { /* the center of the rect */
- *xx = ( pArea->coords[0] + pArea->coords[2] ) / 2;
- *yy = ( pArea->coords[1] + pArea->coords[3] ) / 2;
- return( TRUE );
- }
- break;
-
- case AREA_SHAPE_CIRCLE:
- if ( pArea->coord_cnt >= 3)
- { /* the center of the rect */
- *xx = pArea->coords[0];
- *yy = pArea->coords[1];
- return( TRUE );
- }
- break;
-
- case AREA_SHAPE_POLY:
- if (pArea->coord_cnt >= 6)
- {
- return( lo_find_location_in_poly(xx, yy, pArea->coords, pArea->coord_cnt) );
- }
- break;
-
- case AREA_SHAPE_DEFAULT:
- /* TODO ?????? */
- break;
-
- case AREA_SHAPE_UNKNOWN:
- default:
- break;
- }
-
- return( FALSE );
- }
-
- /* NO_TAB_NAVIGATION */
-
-
-