/* ======================================================= *
* Copyright 1998-2005 Stephen C. Grubb *
* http://ploticus.sourceforge.net *
* Covered by GPL; see the file ./Copyright for details. *
* ======================================================= */
/* MAPFILE - generate a clickable imagemap. All related bookkeeping is here.
Feb 2002 scg client-side HTML clickmap supported
Mar 27 2002 scg SVG clickable map support added (PLS.device == 's') Not implemented here
but rather via SVG_beginobj and SVG_endobj.
*/
#include "pl.h"
#include <string.h>
extern int chmod();
#define MAXENTRIES 500
#define SERVERSIDE 1
#define CLIENTSIDE 2
static int imap;
static char *urls[MAXENTRIES];
static char *titles[MAXENTRIES];
static struct {
char typ;
int pmode;
int x1; int y1; int x2; int y2;
} box[MAXENTRIES];
static char defaulturl[MAXPATH] = "";
static char tpurl[MAXPATH] = ""; /* a url template */
static int mapstatus = 0; /* 1 if we are in the process of doing a map; 0 otherwise */
static int demomode = 0; /* 1 if we are in demo mode */
static int intersect = 0;
extern int PLGS_clickregion();
static int get_targetstr();
/* ========================= */
/* INIT - initialize clickmap facility & make various settings */
int
PL_clickmap_init()
{
PL_clickmap_free(); /* free any previously alloc'ed stores.. */
strcpy( defaulturl, "" );
strcpy( tpurl, "" );
/* debugmode = PLS.debug; */
imap = 0;
intersect = 0;
mapstatus = 1;
return( 0 );
}
/* ========================= */
/* ENTRY - add one map entry */
/* typs: 'r' = rectangle, lower left x,y and upper right x,y; 'p' = point */
int
PL_clickmap_entry( typ, url, pmode, x1, y1, x2, y2, textpad, clipmode, title )
/* mapentry */
char typ;
char *url;
int pmode; /* processing mode; 0 = none, 1 = sub into tpurl as x, 2 = sub into tpurl as y,
3 = intersect w/another then sub into tpurl as x, 4 = intersect then sub as y */
double x1, y1, x2, y2;
int textpad; /* add extra padding to text */
int clipmode; /* 0 = no clip; 1 = clip X to plotting area 2 = clip Y to plotting area */
char *title; /* client-side only.. this will be supplied as the title= attribute - can be large (MAXTT) */
{
int i;
double sx, sy;
if( imap >= MAXENTRIES-1 ) return( Eerr( 2706, "too many clickmap regions, ignoring", url ) );
if( !mapstatus ) return( Eerr( 2707, "-map or -csmap must be specified on command line", "" ) );
if( url[0] == '\0' && title[0] == '\0' ) return( 0 ); /* degenerate case */
/* if( PLS.device == 's' && url[0] == '\0' ) return( 0 ); */ /* mouseover not yet supported for svg */
/* do character conversions within url.. */
for( i = 0; url[i] != '\0'; i++ ) {
if( url[i] == ' ' || url[i] == '\n' ) url[i] = '_';
if( url[i] == '\\' && url[i+1] == 'n' ) { url[i++] = '_'; url[i] = '_'; }
}
urls[ imap ] = (char *) malloc( strlen( url ) + 1 );
strcpy( urls[ imap ], url );
titles[ imap ] = NULL;
if( title[0] != '\0' ) {
int tlen;
tlen = strlen( title );
if( tlen > 0 ) {
titles[ imap ] = (char *) malloc( strlen( title ) + 1 );
strcpy( titles[ imap ], title );
}
}
if( pmode > 2 ) intersect = 1;
box[ imap ].pmode = pmode;
box[ imap ].typ = typ;
if( typ == 'p' ) { x2 = x1; y2 = y1; }
/* adjust for textpad, clip, and global scaling, if any.. */
if( textpad ) { x1 -= 0.15; x2 += 0.1; y1 -= 0.04; }
if( clipmode == 1 ) { if( x1 < EXlo ) x1 = EXlo; if( x2 > EXhi ) x2 = EXhi; }
if( clipmode == 2 ) { if( y1 < EYlo ) y1 = EYlo; if( y2 > EYhi ) y2 = EYhi; }
Egetglobalscale( &sx, &sy );
x1 *= sx; y1 *= sy; x2 *= sx; y2 *= sy;
if( PLS.device == 's' ) {
box[ imap ].x1 = (int)(x1*100); box[ imap ].y1 = (int)(y2*100);
box[ imap ].x2 = (int)(x2*100); box[ imap ].y2 = (int)(y1*100);
}
else {
box[ imap ].x1 = Exsca( x1 ); box[ imap ].y1 = PLG_ysca( y2 );
box[ imap ].x2 = Exsca( x2 ); box[ imap ].y2 = PLG_ysca( y1 );
}
imap++;
return( 0 );
}
/* ========================= */
/* OUT - write out the clickmap info */
/* Note: PLS.clickmap == 1 for server-side, 2 for client-side */
/* SVG: tx and ty may be anything (they aren't used). For SVG the entries must be
in opposite order than image clickmap file, hence the two gotos herein. */
int
PL_clickmap_out( tx, ty )
int tx, ty; /* translate vector - should be 0, 0 for svg */
{
int i, j;
FILE *fp;
char buf[255];
int ox1, oy1, ox2, oy2;
int loopstart, loopend, loopinc;
char targetstr[80];
if( imap < 1 ) return( Eerr( 2795, "Warning, no map regions were assigned", PLS.mapfile ) );
if( PLS.device != 's' ) {
if( stricmp( PLS.mapfile, "stdout" )==0 ) fp = stdout;
else if( stricmp( PLS.mapfile, "stderr" )==0 ) fp = stderr;
else fp = fopen( PLS.mapfile, "w" );
if( fp == NULL ) return( Eerr( 2705, "Cannot open mapfile", PLS.mapfile ));
if( PLS.debug ) fprintf( PLS.diagfp, "writing clickmap file %s, coords translated by %d,%d\n", PLS.mapfile, tx, ty );
if( PLS.clickmap == SERVERSIDE && defaulturl[0] != '\0' ) fprintf( fp, "default %s\n", defaulturl ); /*svg equivalent?*/
if( demomode && PLS.clickmap == CLIENTSIDE ) fprintf( fp, "<map name=\"demo1\">\n" );
}
/* control output ordering (varies according to device) */
if( PLS.device == 's' ) { loopstart = 0; loopend = imap; loopinc = 1; goto DO_THE_REST; } /* svg: "top" is last */
else { loopstart = imap-1; loopend = -1; loopinc = -1; } /* images: "top" is first */
/* first do any intersections.. */
DO_INTERSECTS:
if( intersect ) for( i = loopstart; i != loopend; i += loopinc ) {
if( box[i].pmode == 3 ) {
/* find all '4' entries.. write out an entry for each one found.. */
/* assume all '4' entries overlap all '3' entries.. */
for( j = imap-1; j >= 0; j-- ) {
if( box[j].pmode == 4 ) {
strcpy( buf, tpurl );
GL_varsub( buf, "@XVAL", urls[ i ] );
GL_varsub( buf, "@YVAL", urls[ j ] );
strcpy( targetstr, "" );
get_targetstr( buf, targetstr );
/* take greatest x1 */
if( box[ i ].x1 > box[j].x1 ) ox1 = box[i].x1;
else ox1 = box[j].x1;
/* take smallest x2 */
if( box[ i ].x2 < box[j].x2 ) ox2 = box[i].x2;
else ox2 = box[j].x2;
/* take greatest y1 (smallest for svg) */
if( PLS.device == 's' ) {
if( box[ i ].y1 < box[j].y1 ) oy1 = box[i].y1;
else oy1 = box[j].y1;
}
else {
if( box[ i ].y1 > box[j].y1 ) oy1 = box[i].y1;
else oy1 = box[j].y1;
}
/* take smallest y2 (biggest for svg) */
if( PLS.device == 's' ) {
if( box[ i ].y2 > box[j].y2 ) oy2 = box[i].y2;
else oy2 = box[j].y2;
}
else {
if( box[ i ].y2 < box[j].y2 ) oy2 = box[i].y2;
else oy2 = box[j].y2;
}
#ifndef NOSVG
if( PLS.device == 's' ) PLGS_clickregion( buf, "", targetstr, ox1, oy1, ox2, oy2 );
else
#endif
if( PLS.clickmap == SERVERSIDE )
fprintf( fp, "rect %s %d,%d %d,%d\n", buf, ox1-tx, oy1-ty, ox2-tx, oy2-ty );
else if( PLS.clickmap == CLIENTSIDE )
fprintf( fp, "<area shape=\"rect\" href=\"%s\" coords=\"%d,%d,%d,%d\" %s >\n",
buf, ox1-tx, oy1-ty, ox2-tx, oy2-ty, targetstr );
}
continue;
}
}
}
if( PLS.device == 's' ) return( 0 );
/* now do the rest.. */
DO_THE_REST:
for( i = loopstart; i != loopend; i += loopinc ) {
strcpy( buf, "" );
if( box[ i ].pmode > 0 ) {
strcpy( buf, tpurl );
if( box[ i ].pmode == 1 || box[i].pmode == 3 ) {
GL_varsub( buf, "@XVAL", urls[ i ] );
GL_varsub( buf, "@YVAL", "" );
}
else if( box[ i ].pmode == 2 || box[i].pmode == 4 ) {
GL_varsub( buf, "@YVAL", urls[ i ] );
GL_varsub( buf, "@XVAL", "" );
}
}
else strcpy( buf, urls[i] );
/* handle <area> tag attributes construct eg. [target=xxx] - added scg 5/11/06 */
strcpy( targetstr, "" );
get_targetstr( buf, targetstr );
#ifndef NOSVG
if( PLS.device == 's' ) {
if( titles[i] == NULL ) PLGS_clickregion( buf, "", targetstr, box[ i ].x1, box[ i ].y1, box[ i ].x2, box[ i ].y2 );
else PLGS_clickregion( buf, titles[i], targetstr, box[ i ].x1, box[ i ].y1, box[ i ].x2, box[ i ].y2 );
}
else
#endif
if( PLS.clickmap == SERVERSIDE ) fprintf( fp, "rect %s %d,%d %d,%d\n",
buf, box[ i ].x1 - tx, box[ i ].y1 - ty, box[ i ].x2 - tx, box[ i ].y2 - ty );
else if( PLS.clickmap == CLIENTSIDE ) {
fprintf( fp, "<area shape=\"rect\" %s%s%c coords=\"%d,%d,%d,%d\" ",
(buf[0] == '\0') ? "nohref" : "href=\"" , buf, (buf[0] == '\0' ) ? ' ' : '"',
box[ i ].x1 - tx, box[ i ].y1 - ty, box[ i ].x2 - tx, box[ i ].y2 - ty );
if( titles[i] != NULL ) fprintf( fp, "title=\"%s\" %s >\n", titles[i], targetstr );
else fprintf( fp, "%s >\n", targetstr );
}
}
if( PLS.device == 's' ) goto DO_INTERSECTS;
if( PLS.clickmap == CLIENTSIDE && PLS.device != 's' ) { /* do default */
strcpy( buf, defaulturl );
strcpy( targetstr, "" );
get_targetstr( buf, targetstr );
if( defaulturl[0] != '\0' ) fprintf( fp, "<area shape=\"default\" href=\"%s\" %s >\n", buf, targetstr );
if( demomode ) fprintf( fp, "</map>\n<img src=\"%s\" usemap=\"#demo1\">\n", PLS.outfile );
}
if( PLS.device != 's' && !GL_smember( PLS.mapfile, "stderr stdout" )) {
fclose( fp );
#ifndef WIN32
chmod( PLS.mapfile, 00644 );
#endif
}
return( 0 );
}
/* =========================== */
/* SHOW - display map regions using a green outline - for svg this is done in svg.c */
int
PL_clickmap_show( dev )
char dev;
{
int i;
extern int PLGX_color(), PLGX_linetype(), PLGX_rawline(), PLGG_color(), PLGG_linetype(), PLGG_rawline();
if( PLS.device == 's' ) return( 0 );
if( imap < 1 ) return( Eerr( 2937, "Warning, no map regions were assigned", PLS.mapfile ) );
if( dev == 'x' ) {
#ifndef NOX11
PLGX_color( "brightgreen" );
PLGX_linetype( "0", 0.5, 1.0 );
for( i = 0; i < imap; i++ ) {
if( box[i].typ == 'p' ) { box[i].x2 += 1; box[i].y2 += 1; }
PLGX_rawline( box[i].x1, box[i].y1, box[i].x2, box[i].y1 );
PLGX_rawline( box[i].x2, box[i].y1, box[i].x2, box[i].y2 );
PLGX_rawline( box[i].x2, box[i].y2, box[i].x1, box[i].y2 );
PLGX_rawline( box[i].x1, box[i].y2, box[i].x1, box[i].y1 );
}
#endif
;
}
else if( dev == 'g' ) {
#ifndef NOGD
PLGG_color( "brightgreen" );
PLGG_linetype( "1", 0.5, 1.0 );
for( i = 0; i < imap; i++ ) {
if( box[i].typ == 'p' ) { box[i].x2 += 1; box[i].y2 += 1; }
PLGG_rawline( box[i].x1, box[i].y1, box[i].x2, box[i].y1 );
PLGG_rawline( box[i].x2, box[i].y1, box[i].x2, box[i].y2 );
PLGG_rawline( box[i].x2, box[i].y2, box[i].x1, box[i].y2 );
PLGG_rawline( box[i].x1, box[i].y2, box[i].x1, box[i].y1 );
}
#endif
;
}
return( 0 );
}
/* ========================== */
int
PL_clickmap_free()
{
int i;
if( imap < 1 ) return( 0 );
for( i = 0; i < imap; i++ ) {
free( urls[i] );
if( titles[i] != NULL ) free( titles[i] );
}
imap = 0;
mapstatus = 0;
return( 0 );
}
/* ========================= */
/* INPROGRESS - return 1 if we are doing a clickmap, 0 otherwise */
int
PL_clickmap_inprogress()
{
return( mapstatus );
}
/* ========================= */
/* DEMOMODE - set demo mode */
int
PL_clickmap_demomode( mode )
int mode;
{
demomode = mode;
return( 0 );
}
/* ========================= */
/* GETDEMOMODE - return 1 if we are in demo mode, 0 otherwise */
int
PL_clickmap_getdemomode()
{
return( demomode );
}
/* ========================= */
/* SETDEFAULTURL - set the "default" url */
int
PL_clickmap_setdefaulturl( url )
char *url;
{
strcpy( defaulturl, url );
return( 0 );
}
/* ========================== */
/* SETURLT - set the url template to be used for plot area grid mapping */
int
PL_clickmap_seturlt( url )
char *url;
{
strcpy( tpurl, url );
return( 0 );
}
/* =========================== */
static int
get_targetstr( buf, targetstr )
char *buf, *targetstr;
{
int j;
if( buf[0] == '[' ) {
for( j = 0; buf[j] != '\0'; j++ ) {
if( buf[j] == '[' ) buf[j] = ' ';
if( buf[j] == ']' ) {
buf[j] = ' ';
sscanf( buf, "%s", targetstr );
strcpy( buf, &buf[j+1] );
break;
}
}
}
return( 0 );
}
/* ======================================================= *
* Copyright 1998-2005 Stephen C. Grubb *
* http://ploticus.sourceforge.net *
* Covered by GPL; see the file ./Copyright for details. *
* ======================================================= */
syntax highlighted by Code2HTML, v. 0.9.1