/* ======================================================= * * 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 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, "\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, "\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 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, "\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, "\n", buf, targetstr ); if( demomode ) fprintf( fp, "\n\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. * * ======================================================= */