001/* 002 * (C) Copyright 2006-2008 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Alexandre Russel 018 * 019 * $Id$ 020 */ 021 022package org.nuxeo.ecm.platform.annotations.gwt.client.view.annotater; 023 024import com.google.gwt.user.client.Window; 025import org.nuxeo.ecm.platform.annotations.gwt.client.controler.AnnotationController; 026import org.nuxeo.ecm.platform.annotations.gwt.client.model.AnnotationChangeListener; 027import org.nuxeo.ecm.platform.annotations.gwt.client.model.AnnotationModel; 028import org.nuxeo.ecm.platform.annotations.gwt.client.util.Utils; 029import org.nuxeo.ecm.platform.annotations.gwt.client.util.XPathUtil; 030import org.nuxeo.ecm.platform.annotations.gwt.client.view.NewAnnotationPopup; 031import org.nuxeo.ecm.platform.annotations.gwt.client.view.decorator.ImageDecorator; 032 033import com.allen_sauer.gwt.log.client.Log; 034import com.google.gwt.dom.client.DivElement; 035import com.google.gwt.dom.client.Document; 036import com.google.gwt.dom.client.Element; 037import com.google.gwt.dom.client.ImageElement; 038import com.google.gwt.dom.client.Node; 039import com.google.gwt.user.client.DOM; 040import com.google.gwt.user.client.Event; 041 042/** 043 * @author Alexandre Russel 044 */ 045public class ImageAnnotater extends AbstractAnnotater implements AnnotationChangeListener { 046 private boolean writing = false; 047 048 private boolean processing = false; 049 050 private final ImageDecorator decorator; 051 052 private DivElement divElement; 053 054 private int ax = -1; 055 056 private int ay = -1; 057 058 private int bx = -1; 059 060 private int by = -1; 061 062 private ImageElement image; 063 064 public ImageAnnotater(AnnotationController controller) { 065 super(controller, true); 066 decorator = new ImageDecorator(controller); 067 controller.addModelChangeListener(this); 068 } 069 070 public void refresh() { 071 ax = -1; 072 073 ay = -1; 074 075 bx = -1; 076 077 by = -1; 078 } 079 080 @Override 081 public void onMouseDown(Event event) { 082 super.onMouseDown(event); 083 if (writing /* || processing */) { 084 Log.debug("ImageAnnotater] Ignore mouse down event"); 085 return; 086 } 087 if (processing) { 088 divElement.getParentElement().removeChild(divElement); 089 } 090 image = getRootImage(event); 091 int[] absoluteTopLeft = Utils.getAbsoluteTopLeft(image, Document.get()); 092 ax = event.getClientX() - absoluteTopLeft[1] + Window.getScrollLeft(); 093 ay = event.getClientY() - absoluteTopLeft[0] + Window.getScrollTop(); 094 bx = ax; 095 by = ay; 096 writing = true; 097 processing = true; 098 controller.disablePopupListeners(); 099 addMap(ax, ay, bx, by, image); 100 } 101 102 private ImageElement getRootImage(Event event) { 103 com.google.gwt.dom.client.Element targetElement = event.getTarget(); 104 ImageElement imageElement = ImageElement.as(targetElement.getOwnerDocument().getElementById( 105 "annotationRootImage")); 106 if (imageElement == null) { 107 if (targetElement.getNodeName().equalsIgnoreCase("img")) { 108 imageElement = ImageElement.as(targetElement); 109 } else if (targetElement.getNodeName().equalsIgnoreCase("div")) { 110 imageElement = getImageElementFromAnchor(targetElement); 111 } 112 } 113 return imageElement; 114 } 115 116 private static ImageElement getImageElementFromAnchor(com.google.gwt.dom.client.Element anchorElement) { 117 Node element; 118 while ((element = anchorElement.getPreviousSibling()) != null) { 119 Log.debug("getImageElementFromAnchor -- nodeName: " + element.getNodeName()); 120 if (element.getNodeName().equalsIgnoreCase("img")) { 121 return ImageElement.as((Element) element.cast()); 122 } 123 } 124 return null; 125 } 126 127 @Override 128 public void onMouseMove(Event event) { 129 super.onMouseMove(event); 130 131 if (!writing) { 132 return; 133 } 134 String nodeName = event.getTarget().getNodeName(); 135 if (nodeName.equalsIgnoreCase("img")) { 136 ImageElement newImage = ImageElement.as(event.getTarget()); 137 if ((!image.equals(newImage) || ax == -1 || ay == -1) && !controller.isMultiImage()) { 138 refresh(); 139 } 140 } 141 int[] absoluteTopLeft = Utils.getAbsoluteTopLeft(image, Document.get()); 142 bx = event.getClientX() - absoluteTopLeft[1] + Window.getScrollLeft(); 143 by = event.getClientY() - absoluteTopLeft[0] + Window.getScrollTop(); 144 updateMap(ax, ay, bx, by, image); 145 } 146 147 @Override 148 public void onMouseUp(Event event) { 149 if (!hasMoved() && writing) { 150 Log.debug("cancel mouse up image"); 151 cancelMap(); 152 controller.setNewAnnotationPopup(null); 153 if (controller.isAnnotationsVisible()) { 154 controller.enablePopupListeners(); 155 } 156 super.onMouseUp(event); 157 return; 158 } 159 160 super.onMouseUp(event); 161 162 if (!writing) { 163 return; 164 } 165 String nodeName = event.getTarget().getNodeName(); 166 if (nodeName.equalsIgnoreCase("img")) { 167 ImageElement newImage = ImageElement.as(event.getTarget()); 168 if ((!image.equals(newImage) || ax == -1 || ay == -1) && !controller.isMultiImage()) { 169 refresh(); 170 } 171 } 172 173 int[] absoluteTopLeft = Utils.getAbsoluteTopLeft(image, Document.get()); 174 bx = event.getClientX() - absoluteTopLeft[1] + Window.getScrollLeft(); 175 by = event.getClientY() - absoluteTopLeft[0] + Window.getScrollTop(); 176 addMapAndGetAnnot(new int[] { ax, ay, bx, by }, image); 177 if (controller.isAnnotationsVisible()) { 178 controller.enablePopupListeners(); 179 } 180 writing = false; 181 addAnnotationPopup(); 182 controller.enablePopupListeners(); 183 } 184 185 private void cancelMap() { 186 writing = false; 187 processing = false; 188 if (divElement != null) { 189 DOM.setEventListener((com.google.gwt.user.client.Element) divElement.cast(), null); 190 Log.debug("Parent element: " + divElement.getParentElement()); 191 if (divElement.getParentElement() != null) { 192 divElement.getParentElement().removeChild(divElement); 193 } 194 } 195 } 196 197 public void updateMap(int ax2, int ay2, int bx2, int by2, ImageElement img) { 198 decorator.updateAnnotatedArea(ax2, ay2, bx2, by2, img, divElement); 199 } 200 201 private void addMap(int ax2, int ay2, int ax3, int ay3, ImageElement img) { 202 divElement = decorator.addAnnotatedArea(ax, ay, bx, by, this); 203 } 204 205 public void addMapAndGetAnnot(int[] points, ImageElement img) { 206 DOM.setEventListener((com.google.gwt.user.client.Element) divElement.cast(), null); 207 String xpath = img.getParentElement().getId(); 208 xpath = XPathUtil.fromIdableName(xpath); 209 checkInt(points); 210 String xpointer = controller.filterXPointer(image, xpath, points[0], points[1], points[2], points[3]); 211 Log.debug("XPointer: " + xpointer); 212 controller.createNewAnnotation(xpointer); 213 NewAnnotationPopup popup = new NewAnnotationPopup(divElement, controller, true, "local"); 214 controller.setNewAnnotationPopup(popup); 215 } 216 217 private static void checkInt(int[] points) { 218 // following code is because, on some IE machine we got float instead of 219 // integer: 220 for (int x = 0; x < points.length; x++) { 221 points[x] = ("" + points[x]).contains(".") ? Integer.parseInt(("" + points[x]).substring(0, 222 ("" + points[x]).indexOf("."))) : points[x]; 223 } 224 } 225 226 public ImageElement getImage() { 227 return image; 228 } 229 230 public void setImage(ImageElement image) { 231 this.image = image; 232 } 233 234 public void updateMap(int bx2, int by2, ImageElement image2) { 235 decorator.updateAnnotatedArea(ax, ay, bx2, by2, image2, divElement); 236 } 237 238 public void onChange(AnnotationModel model, ChangeEvent ce) { 239 if (model.getNewAnnotation() == null && ce == ChangeEvent.annotation) { 240 processing = false; 241 } 242 } 243 244}