UsemapImage add-on

I used to implement clickable chart by image tag and usemap tag in my old web project, when i migrate to vaadin platform, i couldn’t find a component supoorts both image and usemap, so i made a add-on widget, hope it could be useful to someone else who have a same problem as me

Vaadin Component:

@com.vaadin.ui.ClientWidget(com.fab.yms.web.common.client.ui.VUsemapImage.class)
public class UsemapImage extends AbstractComponent
{
/**
*
*/
private static final long serialVersionUID = -6768105755082174490L;

private Resource image;
private String usemap;

public void setUsemapImage(Resource image, String usemap)
{
	if(image != null)
	{
		String mt = image.getMIMEType();
		if ((mt.substring(0, mt.indexOf("/")).equalsIgnoreCase("image")))
		{
			this.image = image;
			this.usemap = usemap;
			requestRepaint();
		}
		else
		{
			throw new IllegalArgumentException(mt + " is not a valid image type");
		}
	}
}

@Override
public void paintContent(PaintTarget target) throws PaintException
{
	super.paintContent(target);
	if (image != null)
	{
		target.addAttribute(VUsemapImage.ATTRIBUTE_IMAGE, image);
		if (usemap != null)
			target.addAttribute(VUsemapImage.ATTRIBUTE_USEMAP, usemap);
	}
}

/**
 * Receive and handle events and other variable changes from the client.
 * 
 * {@inheritDoc}
 */
@Override
public void changeVariables(Object source, Map<String, Object> variables)
{
	super.changeVariables(source, variables);

	if (variables.containsKey(VUsemapImage.EVENT_ID_MOUSE_UP))
	{
		Object obj = variables.get(VUsemapImage.EVENT_ID_MOUSE_UP);
		if(obj instanceof Object[])
		{
			Object[] params = (Object[]

)obj;
AreaClickEvent event = new AreaClickEvent(this, params);
fireEvent(event);
requestRepaint();
}
}
}

public interface AreaClickListener extends ComponentEventListener
{
	public static final Method ONCLICK_METHOD = ReflectTools.findMethod(
			AreaClickListener.class, "onClick", AreaClickEvent.class);

	public void onClick(AreaClickEvent event);
}

public void addListener(AreaClickListener listener)
{
	addListener(AreaClickEvent.class, listener, AreaClickListener.ONCLICK_METHOD);
}

public void removeListener(AreaClickListener listener)
{
	removeListener(AreaClickEvent.class, listener, AreaClickListener.ONCLICK_METHOD);
}

public class AreaClickEvent extends Component.Event
{
	/**
	 * 
	 */
	private static final long serialVersionUID = 8754823991334501952L;
	private int x, y;
	private String mouseButton, id, title, alt, shape, coords, href, target;//, data;

	public AreaClickEvent(Component source, Object[] area)
	{
		super(source);
		
		x = (Integer)area[VUsemapImage.IDX_MOUSE_CLICK_X]

;
y = (Integer)area[VUsemapImage.IDX_MOUSE_CLICK_Y]
;
mouseButton = (String)area[VUsemapImage.IDX_MOUSE_BUTTONG]
;
id = (String)area[VUsemapImage.IDX_ID]
;
title = (String)area[VUsemapImage.IDX_TITLE]
;
alt = (String)area[VUsemapImage.IDX_ALT]
;
shape = (String)area[VUsemapImage.IDX_SHAPE]
;
coords = (String)area[VUsemapImage.IDX_COORDS]
;
href = (String)area[VUsemapImage.IDX_HREF]
;
target = (String)area[VUsemapImage.IDX_TARGET]
;
}

	public int getX()
	{
		return x;
	}

	public int getY()
	{
		return y;
	}

	public String getMouseButton()
	{
		return mouseButton;
	}

	public String getId()
	{
		return id;
	}

	public String getTitle()
	{
		return title;
	}

	public String getAlt()
	{
		return alt;
	}

	public String getShape()
	{
		return shape;
	}

	public String getCoords()
	{
		return coords;
	}

	public String getHref()
	{
		return href;
	}

	public String getTarget()
	{
		return target;
	}
}

}

GWT component

public class VUsemapImage extends HTML implements Paintable, MouseUpHandler
{
public static final String ATTRIBUTE_IMAGE = “image”;
public static final String ATTRIBUTE_USEMAP = “usemap”;

public static final String EVENT_ID_MOUSE_UP = "click";

public static final int IDX_MOUSE_BUTTONG = 0;
public static final int IDX_MOUSE_CLICK_X = 1;
public static final int IDX_MOUSE_CLICK_Y = 2;
public static final int IDX_ID = 3;
public static final int IDX_TITLE = 4;
public static final int IDX_ALT = 5;
public static final int IDX_SHAPE = 6;
public static final int IDX_COORDS = 7;
public static final int IDX_HREF = 8;
public static final int IDX_TARGET = 9;

/** Set the CSS class name to allow styling. */
public static final String CLASSNAME = "v-usemapimage";

/** The client side widget identifier */
protected String paintableId;

/** Reference to the server connection object. */
protected ApplicationConnection client;

protected NodeList<AreaElement> areas;

protected AreaElement getArea(int x, int y)
{
	if (areas == null || areas.getLength() == 0)
		return null;

	AreaElement area = null;
	for (int i = 0; i < areas.getLength(); i++)
	{
		AreaElement a = areas.getItem(i);
		if (XYinArea(x, y, a))
		{
			area = a;
			break;
		}
	}

	return area;
}

protected boolean XYinArea(int x, int y, AreaElement area)
{
	String shape = area.getShape();
	List<Double> coords = getCoordsList(area.getCoords());

	boolean result = false;
	if (shape.compareToIgnoreCase("rect") == 0)
	{
		result = XYinRect(x, y, coords);
	} else if (shape.compareToIgnoreCase("circle") == 0)
	{
		result = XYinCircle(x, y, coords);
	} else if (shape.compareToIgnoreCase("poly") == 0)
	{
		result = XYinPoly(x, y, coords);
	}

	return result;
}

protected List<Double> getCoordsList(String coords)
{
	List<Double> lst = new LinkedList<Double>();
	String[] ss = coords.split(",");
	if (ss == null)
		return lst;

	for (String s : ss)
	{
		lst.add(Double.valueOf(s));
	}

	return lst;
}

protected boolean XYinRect(int x, int y, List<Double> lstCoord)
{
	int x1 = lstCoord.get(0).intValue();
	int y1 = lstCoord.get(1).intValue();
	int x2 = lstCoord.get(2).intValue();
	int y2 = lstCoord.get(3).intValue();

	if (x >= x1 && x <= x2 && y >= y1 && y <= y2)
		return true;
	else
		return false;
}

protected boolean XYinCircle(int x, int y, List<Double> lstCoord)
{
	int x1 = lstCoord.get(0).intValue();
	int y1 = lstCoord.get(1).intValue();
	int r = lstCoord.get(2).intValue();

	if (((x - x1) * (x - x1) + (y - y1) * (y - y1)) <= r * r)
		return true;
	else
		return false;
}

protected boolean XYinPoly(int x, int y, List<Double> lstCoord)
{
	double x1 = lstCoord.get(0);
	double y1 = lstCoord.get(1);
	double xn = lstCoord.get(lstCoord.size() - 2);
	double yn = lstCoord.get(lstCoord.size() - 1);

	if (x1 != xn || y1 != yn)
	{
		lstCoord.add(x1);
		lstCoord.add(y1);
	}

	int polySides = lstCoord.size() / 2;
	double[] polyX = new double[polySides]

, polyY = new double[polySides]
;
for (int i = 0; i < lstCoord.size(); i++)
{
double v = lstCoord.get(i);
if (i % 2 == 0)
polyX[i / 2]
= v;
else
polyY[i / 2]
= v;
}

	return pointInPolygon(polySides, polyX, polyY, x, y);
}

/**
 * from http://alienryderflex.com/polygon/
 * 
 * @param polySides
 *            how many corners the polygon has
 * @param polyX
 *            horizontal coordinates of corners
 * @param polyY
 *            vertical coordinates of corners
 * @param x
 *            point to be tested
 * @param y
 *            point to be tested
 * @return true if the point x,y is inside the polygon
 */
protected boolean pointInPolygon(int polySides, double polyX[],
		double polyY[], double x, double y)
{

	int i, j = polySides - 1;
	boolean oddNodes = false;

	for (i = 0; i < polySides; i++)
	{
		if (polyY[i]

< y && polyY[j]

= y || polyY[j]
< y && polyY[i]
= y)
{
if (polyX[i]

  • (y - polyY[i]
    ) / (polyY[j]
  • polyY[i]
    )
    * (polyX[j]

  • polyX[i]
    ) < x)
    {
    oddNodes = !oddNodes;
    }
    }
    j = i;
    }

     return oddNodes;
    

    }

    /**

    • The constructor should first call super() to initialize the component and

    • then handle any initialization relevant to Vaadin.
      */
      public VUsemapImage()
      {
      super();
      setElement(Document.get().createDivElement());

      // This method call of the Paintable interface sets the component
      // style name in DOM tree
      setStyleName(CLASSNAME);

      // Tell GWT we are interested in receiving click events
      // sinkEvents(Event.ONCLICK);
      // Add a handler for the click events (this is similar to
      // FocusWidget.addClickHandler())
      // addDomHandler(this, ClickEvent.getType());

      sinkEvents(Event.ONMOUSEUP);
      addDomHandler(this, MouseUpEvent.getType());
      }

    /**

    • Called whenever an update is received from the server
      */
      public void updateFromUIDL(UIDL uidl, ApplicationConnection client)
      {
      if (client.updateComponent(this, uidl, true))
      return;

      this.client = client;

      // Save the client side identifier (paintable id) for the widget
      paintableId = uidl.getId();

      // clickEventHandler.handleEventHandlerRegistration(client);

      if (uidl.hasAttribute(ATTRIBUTE_IMAGE))
      {
      areas = null;
      //setHTML(“”);

       String url = client.translateVaadinUri(uidl.getStringAttribute(ATTRIBUTE_IMAGE));
       if (url == null) url = "";
       
       Element elm_img = null, elm_map = null;
       NodeList<Node> nodes = getElement().getChildNodes();
       if (nodes != null && nodes.getLength() > 0 )
       {
       	for(int i=0; i < nodes.getLength(); i++)
       	{
       		Node n = nodes.getItem(i);
       		if (n.getNodeType() != Node.ELEMENT_NODE) continue;
       		Element e = (Element) n;
       		if (e.getTagName().equalsIgnoreCase("IMG"))
       		{
       			elm_img = e;
       		}
       		else if(e.getTagName().equalsIgnoreCase("MAP"))
       		{
       			elm_map = e;
       		}
       	}
       }
       
       boolean created_img = false;
       if(elm_img == null)
       {
       	elm_img = DOM.createImg();
       	created_img = true;
       	client.addPngFix(elm_img);
       	DOM.sinkEvents(elm_img, Event.ONLOAD);
       }
       
       // Set attributes
       Style style = elm_img.getStyle();
       String w = uidl.getStringAttribute("width");
       if (w != null)
       {
       	style.setProperty("width", w);
       } else
       {
       	style.setProperty("width", "");
       }
       String h = uidl.getStringAttribute("height");
       if (h != null)
       {
       	style.setProperty("height", h);
       } else
       {
       	style.setProperty("height", "");
       }
       
       DOM.setElementProperty(elm_img, "src", url);
       
       if (uidl.hasAttribute(ATTRIBUTE_USEMAP))
       {
       	String usemap = uidl.getStringAttribute(ATTRIBUTE_USEMAP);
       	if(usemap!=null && usemap.length()>0)
       	{
       		boolean created_map = false;
       		if(elm_map == null)
       		{
       			elm_map = DOM.createElement("map");
       			created_map = true;
       		}
       		elm_map.setInnerHTML(usemap);
       		areas = MapElement.as(elm_map).getAreas();
       		
       		String filename = url.substring(url.lastIndexOf('/')+1);
       		if(filename!=null && filename.length()>0)
       		{
       			/**
       			 * TODO, GWT's problem, see
       			 * http://code.google.com/p/google-web-toolkit/issues/detail?id=3041
       			 */
       			DOM.setElementProperty(elm_img, "useMap", "#" + filename);
       			DOM.setElementProperty(elm_map, "name", filename);
       			if (created_map)
       			{
       				getElement().appendChild(elm_map);
       			}
       		}
       	}
       }
       
       if (created_img)
       {
       	getElement().appendChild(elm_img);
       }
      
       /*
        * Sink tooltip events so tooltip is displayed when hovering the
        * image.
        */
       sinkEvents(VTooltip.TOOLTIP_EVENTS);
      

      }
      }

    @Override
    public void onMouseUp(MouseUpEvent event)
    {
    String b = null;
    switch (event.getNativeButton())
    {
    case NativeEvent.BUTTON_LEFT:
    b = “left”;
    break;
    case NativeEvent.BUTTON_MIDDLE:
    b = “middle”;
    break;
    case NativeEvent.BUTTON_RIGHT:
    b = “right”;
    break;
    default:
    b = “unknown”;
    }

     int x = event.getX(), y = event.getY();
     
     Object[] map = new Object[10]
    

;
map[IDX_MOUSE_BUTTONG]
= b;
map[IDX_MOUSE_CLICK_X]
= x;
map[IDX_MOUSE_CLICK_Y]
= y;

	AreaElement area = getArea(x, y);
	if (area != null)
	{
		map[IDX_ID]

= area.getId();
map[IDX_TITLE]
= area.getTitle();
map[IDX_ALT]
= area.getAlt();
map[IDX_SHAPE]
= area.getShape();
map[IDX_COORDS]
= area.getCoords();
map[IDX_HREF]
= area.getHref();
map[IDX_TARGET]
= area.getTarget();
}

	client.updateVariable(paintableId, EVENT_ID_MOUSE_UP, map, true);
}

}

How about publishing it in the
Vaadin Directory
- that would lower the threshold of taking it into use significantly, and should not require that much effort. There are instructions for the authors (check the links on the left in the directory).

yes, it is submitted. thank you.