/* * (C) Copyright 2006-2008 Nuxeo SA (http://nuxeo.com/) and others. * * 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. * * Contributors: * Alexandre Russel * * $Id$ */ package org.nuxeo.ecm.platform.annotations.gwt.client.view.annotater; import com.google.gwt.user.client.Window; import org.nuxeo.ecm.platform.annotations.gwt.client.controler.AnnotationController; import org.nuxeo.ecm.platform.annotations.gwt.client.model.AnnotationChangeListener; import org.nuxeo.ecm.platform.annotations.gwt.client.model.AnnotationModel; import org.nuxeo.ecm.platform.annotations.gwt.client.util.Utils; import org.nuxeo.ecm.platform.annotations.gwt.client.util.XPathUtil; import org.nuxeo.ecm.platform.annotations.gwt.client.view.NewAnnotationPopup; import org.nuxeo.ecm.platform.annotations.gwt.client.view.decorator.ImageDecorator; import com.allen_sauer.gwt.log.client.Log; import com.google.gwt.dom.client.DivElement; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.ImageElement; import com.google.gwt.dom.client.Node; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Event; /** * @author Alexandre Russel */ public class ImageAnnotater extends AbstractAnnotater implements AnnotationChangeListener { private boolean writing = false; private boolean processing = false; private final ImageDecorator decorator; private DivElement divElement; private int ax = -1; private int ay = -1; private int bx = -1; private int by = -1; private ImageElement image; public ImageAnnotater(AnnotationController controller) { super(controller, true); decorator = new ImageDecorator(controller); controller.addModelChangeListener(this); } public void refresh() { ax = -1; ay = -1; bx = -1; by = -1; } @Override public void onMouseDown(Event event) { super.onMouseDown(event); if (writing /* || processing */) { Log.debug("ImageAnnotater] Ignore mouse down event"); return; } if (processing) { divElement.getParentElement().removeChild(divElement); } image = getRootImage(event); int[] absoluteTopLeft = Utils.getAbsoluteTopLeft(image, Document.get()); ax = event.getClientX() - absoluteTopLeft[1] + Window.getScrollLeft(); ay = event.getClientY() - absoluteTopLeft[0] + Window.getScrollTop(); bx = ax; by = ay; writing = true; processing = true; controller.disablePopupListeners(); addMap(ax, ay, bx, by, image); } private ImageElement getRootImage(Event event) { com.google.gwt.dom.client.Element targetElement = event.getTarget(); ImageElement imageElement = ImageElement.as(targetElement.getOwnerDocument().getElementById( "annotationRootImage")); if (imageElement == null) { if (targetElement.getNodeName().equalsIgnoreCase("img")) { imageElement = ImageElement.as(targetElement); } else if (targetElement.getNodeName().equalsIgnoreCase("div")) { imageElement = getImageElementFromAnchor(targetElement); } } return imageElement; } private static ImageElement getImageElementFromAnchor(com.google.gwt.dom.client.Element anchorElement) { Node element; while ((element = anchorElement.getPreviousSibling()) != null) { Log.debug("getImageElementFromAnchor -- nodeName: " + element.getNodeName()); if (element.getNodeName().equalsIgnoreCase("img")) { return ImageElement.as((Element) element.cast()); } } return null; } @Override public void onMouseMove(Event event) { super.onMouseMove(event); if (!writing) { return; } String nodeName = event.getTarget().getNodeName(); if (nodeName.equalsIgnoreCase("img")) { ImageElement newImage = ImageElement.as(event.getTarget()); if ((!image.equals(newImage) || ax == -1 || ay == -1) && !controller.isMultiImage()) { refresh(); } } int[] absoluteTopLeft = Utils.getAbsoluteTopLeft(image, Document.get()); bx = event.getClientX() - absoluteTopLeft[1] + Window.getScrollLeft(); by = event.getClientY() - absoluteTopLeft[0] + Window.getScrollTop(); updateMap(ax, ay, bx, by, image); } @Override public void onMouseUp(Event event) { if (!hasMoved() && writing) { Log.debug("cancel mouse up image"); cancelMap(); controller.setNewAnnotationPopup(null); if (controller.isAnnotationsVisible()) { controller.enablePopupListeners(); } super.onMouseUp(event); return; } super.onMouseUp(event); if (!writing) { return; } String nodeName = event.getTarget().getNodeName(); if (nodeName.equalsIgnoreCase("img")) { ImageElement newImage = ImageElement.as(event.getTarget()); if ((!image.equals(newImage) || ax == -1 || ay == -1) && !controller.isMultiImage()) { refresh(); } } int[] absoluteTopLeft = Utils.getAbsoluteTopLeft(image, Document.get()); bx = event.getClientX() - absoluteTopLeft[1] + Window.getScrollLeft(); by = event.getClientY() - absoluteTopLeft[0] + Window.getScrollTop(); addMapAndGetAnnot(new int[] { ax, ay, bx, by }, image); if (controller.isAnnotationsVisible()) { controller.enablePopupListeners(); } writing = false; addAnnotationPopup(); controller.enablePopupListeners(); } private void cancelMap() { writing = false; processing = false; if (divElement != null) { DOM.setEventListener((com.google.gwt.user.client.Element) divElement.cast(), null); Log.debug("Parent element: " + divElement.getParentElement()); if (divElement.getParentElement() != null) { divElement.getParentElement().removeChild(divElement); } } } public void updateMap(int ax2, int ay2, int bx2, int by2, ImageElement img) { decorator.updateAnnotatedArea(ax2, ay2, bx2, by2, img, divElement); } private void addMap(int ax2, int ay2, int ax3, int ay3, ImageElement img) { divElement = decorator.addAnnotatedArea(ax, ay, bx, by, this); } public void addMapAndGetAnnot(int[] points, ImageElement img) { DOM.setEventListener((com.google.gwt.user.client.Element) divElement.cast(), null); String xpath = img.getParentElement().getId(); xpath = XPathUtil.fromIdableName(xpath); checkInt(points); String xpointer = controller.filterXPointer(image, xpath, points[0], points[1], points[2], points[3]); Log.debug("XPointer: " + xpointer); controller.createNewAnnotation(xpointer); NewAnnotationPopup popup = new NewAnnotationPopup(divElement, controller, true, "local"); controller.setNewAnnotationPopup(popup); } private static void checkInt(int[] points) { // following code is because, on some IE machine we got float instead of // integer: for (int x = 0; x < points.length; x++) { points[x] = ("" + points[x]).contains(".") ? Integer.parseInt(("" + points[x]).substring(0, ("" + points[x]).indexOf("."))) : points[x]; } } public ImageElement getImage() { return image; } public void setImage(ImageElement image) { this.image = image; } public void updateMap(int bx2, int by2, ImageElement image2) { decorator.updateAnnotatedArea(ax, ay, bx2, by2, image2, divElement); } public void onChange(AnnotationModel model, ChangeEvent ce) { if (model.getNewAnnotation() == null && ce == ChangeEvent.annotation) { processing = false; } } }