001/* 002 * (C) Copyright 2006-2008 Nuxeo SAS (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Alexandre Russel 016 * 017 * $Id$ 018 */ 019package org.nuxeo.ecm.platform.annotations.gwt.client.controler; 020 021import java.util.ArrayList; 022import java.util.List; 023 024import org.nuxeo.ecm.platform.annotations.gwt.client.AnnotationConstant; 025import org.nuxeo.ecm.platform.annotations.gwt.client.annotea.AnnoteaClient; 026import org.nuxeo.ecm.platform.annotations.gwt.client.configuration.WebConfiguration; 027import org.nuxeo.ecm.platform.annotations.gwt.client.model.Annotation; 028import org.nuxeo.ecm.platform.annotations.gwt.client.model.AnnotationChangeListener; 029import org.nuxeo.ecm.platform.annotations.gwt.client.model.AnnotationModel; 030import org.nuxeo.ecm.platform.annotations.gwt.client.util.Point; 031import org.nuxeo.ecm.platform.annotations.gwt.client.util.StringRangeXPointer; 032import org.nuxeo.ecm.platform.annotations.gwt.client.util.XPointerFactory; 033import org.nuxeo.ecm.platform.annotations.gwt.client.view.AnnotatedDocument; 034import org.nuxeo.ecm.platform.annotations.gwt.client.view.NewAnnotationPopup; 035import org.nuxeo.ecm.platform.annotations.gwt.client.view.listener.AnnotationPopupEventListener; 036 037import com.allen_sauer.gwt.log.client.Log; 038import com.google.gwt.core.client.GWT; 039import com.google.gwt.dom.client.ImageElement; 040import com.google.gwt.user.client.Window; 041 042/** 043 * @author Alexandre Russel 044 */ 045public class AnnotationController { 046 047 private static AnnotationController CURRENT_INSTANCE; 048 049 private static AnnotationModel model = new AnnotationModel(); 050 051 private static NewAnnotationPopup newAnnotationPopup; 052 053 private static AnnotatedDocument annotatedDocument; 054 055 private static final List<AnnotationPopupEventListener> annotationPopupListeners = new ArrayList<AnnotationPopupEventListener>(); 056 057 private String xPointerFilter; 058 059 private String pointerAdapter; 060 061 private boolean annotateImageOnly; 062 063 private boolean multiImage; 064 065 private final AnnoteaClient annoteaClient; 066 067 private final WebConfiguration webConfiguration; 068 069 private int frameScrollFromTop; 070 071 private boolean creationPopupOpened = false; 072 073 private final boolean onFrame; 074 075 public AnnotationController(WebConfiguration webConfiguration, boolean onFrame) { 076 this.onFrame = onFrame; 077 this.webConfiguration = (webConfiguration == null ? WebConfiguration.DEFAULT_WEB_CONFIGURATION 078 : webConfiguration); 079 annoteaClient = new AnnoteaClient(this); 080 081 CURRENT_INSTANCE = this; 082 083 if (onFrame) { 084 annotatedDocument = new AnnotatedDocument(this); 085 model.addChangeListener(annotatedDocument); 086 registerOnFrameMethods(); 087 } else { 088 registerMainModuleMethods(); 089 } 090 091 } 092 093 private native void registerOnFrameMethods() /*-{ 094 top['addNewAnnotation'] = this.@org.nuxeo.ecm.platform.annotations.gwt.client.controler.AnnotationController::addNewAnnotation(); 095 top['showAnnotations'] = this.@org.nuxeo.ecm.platform.annotations.gwt.client.controler.AnnotationController::showAnnotations(); 096 top['hideAnnotations'] = this.@org.nuxeo.ecm.platform.annotations.gwt.client.controler.AnnotationController::hideAnnotations(); 097 top['updateSelectedAnnotation'] = this.@org.nuxeo.ecm.platform.annotations.gwt.client.controler.AnnotationController::updateSelectedAnnotation(I); 098 top['cancelNewAnnotationPopup'] = this.@org.nuxeo.ecm.platform.annotations.gwt.client.controler.AnnotationController::cancelNewAnnotationPopup(); 099 top['loadAnnotationsOnFrame'] = this.@org.nuxeo.ecm.platform.annotations.gwt.client.controler.AnnotationController::loadAnnotations(); 100 top['isAnnotationsVisible'] = this.@org.nuxeo.ecm.platform.annotations.gwt.client.controler.AnnotationController::isAnnotationsVisible(); 101 top['deleteAnnotation'] = this.@org.nuxeo.ecm.platform.annotations.gwt.client.controler.AnnotationController::deleteAnnotationOnFrame(I); 102 }-*/; 103 104 private native void registerMainModuleMethods() /*-{ 105 top['loadAnnotationsOnMainModule'] = this.@org.nuxeo.ecm.platform.annotations.gwt.client.controler.AnnotationController::loadAnnotations(); 106 }-*/; 107 108 public void addNewAnnotation() { 109 NewAnnotationPopup popup = getNewAnnotationPopup(); 110 Annotation annotation = getNewAnnotation(); 111 if (popup != null && annotation != null) { 112 if (annotation.getXpointer() instanceof StringRangeXPointer) { 113 // pre-decorate the selected text 114 annotatedDocument.decorateSelectedText(annotation); 115 } 116 popup.show(); 117 } 118 } 119 120 public WebConfiguration getWebConfiguration() { 121 return webConfiguration; 122 } 123 124 public AnnotatedDocument getAnnotatedDocument() { 125 return annotatedDocument; 126 } 127 128 public void setXPointerFilter(String pointerFilter) { 129 xPointerFilter = pointerFilter; 130 } 131 132 public void setPointerAdapter(String pointerAdapter) { 133 this.pointerAdapter = pointerAdapter; 134 } 135 136 public boolean canAnnotate() { 137 return webConfiguration.canAnnotate(); 138 } 139 140 public void addModelChangeListener(AnnotationChangeListener listener) { 141 model.addChangeListener(listener); 142 } 143 144 public int getFrameScrollFromTop() { 145 return frameScrollFromTop; 146 } 147 148 public void setFrameScrollFromTop(int frameScrollFromTop) { 149 this.frameScrollFromTop = frameScrollFromTop; 150 } 151 152 public boolean canCreateNewCreationPopup() { 153 return !creationPopupOpened; 154 } 155 156 public void openCreationPopup() { 157 creationPopupOpened = true; 158 } 159 160 public void closeCreationPopup() { 161 creationPopupOpened = false; 162 } 163 164 public void newAnnotationCreated(Annotation annotation) { 165 model.setNewAnnotation(annotation); 166 } 167 168 public void submitNewAnnotation() { 169 GWT.log("submiting new annotation", null); 170 Annotation newAnnotation = model.getNewAnnotation(); 171 annoteaClient.submitAnnotation(newAnnotation); 172 model.setNewAnnotation(null); 173 174 // set the selected annotation to the newly created one 175 setSelectedAnnotationIndex(model.getAnnotations().size()); 176 } 177 178 private native void setSelectedAnnotationIndex(int index) /*-{ 179 top['selectedAnnotationIndex'] = index; 180 }-*/; 181 182 public void reloadAnnotations() { 183 if (onFrame) { 184 reloadAnnotationsOnMainModule(); 185 } else { 186 reloadAnnotationsOnFrame(); 187 } 188 } 189 190 private native void reloadAnnotationsOnMainModule() /*-{ 191 top['loadAnnotationsOnMainModule'](); 192 }-*/; 193 194 private native void reloadAnnotationsOnFrame() /*-{ 195 top['loadAnnotationsOnFrame'](); 196 }-*/; 197 198 public void cancelNewAnnotation() { 199 model.setNewAnnotation(null); 200 } 201 202 public void cancelNewAnnotationPopup() { 203 if (newAnnotationPopup != null) { 204 newAnnotationPopup.cancel(); 205 } 206 } 207 208 public void createNewAnnotation(String pointer) { 209 String href = getDocumentUrl(); 210 // Hardcoded url codec.... 211 String xpointerURI = href.substring(0, href.lastIndexOf("@") + 1) + pointer; 212 Annotation newAnnotation = new Annotation(XPointerFactory.getXPointer(xpointerURI)); 213 model.setNewAnnotation(newAnnotation); 214 } 215 216 public void setAnnotationList(List<Annotation> annotations) { 217 model.setAnnotations(annotations); 218 } 219 220 public native String getAnnoteaServerUrl() /*-{ 221 return top['annoteaServerUrl']; 222 }-*/; 223 224 public Annotation getNewAnnotation() { 225 return model.getNewAnnotation(); 226 } 227 228 public void loadAnnotations() { 229 if (onFrame) { 230 if (!isMultiImage()) { 231 annotatedDocument.preDecorateDocument(); 232 } 233 } 234 annoteaClient.getAnnotationList(getDocumentUrl()); 235 } 236 237 public native String getDocumentUrl() /*-{ 238 return top['docUrl']; 239 }-*/; 240 241 public void decorateDocument() { 242 Log.debug("decorate document"); 243 annotatedDocument.preDecorateDocument(); 244 updateAnnotation(true); 245 } 246 247 public void updateSelectedAnnotation(int index) { 248 annotatedDocument.updateSelectedAnnotation(index); 249 } 250 251 public void setFirstAnnotationSelected() { 252 if (!model.getAnnotations().isEmpty()) { 253 annotatedDocument.updateSelectedAnnotation(0); 254 } 255 } 256 257 public void setCancelNewAnnotation() { 258 model.setNewAnnotation(null); 259 } 260 261 public void setImageOnly(boolean b) { 262 this.annotateImageOnly = b; 263 } 264 265 public boolean isImageOnly() { 266 return this.annotateImageOnly; 267 } 268 269 public void setMultiImage(boolean b) { 270 this.multiImage = b; 271 } 272 273 public boolean isMultiImage() { 274 return this.multiImage; 275 } 276 277 public String filterXPointer(ImageElement image, String xpath, int i, int j, int k, int l) { 278 if (xPointerFilter != null) { 279 return filter(xPointerFilter, image, xpath, i, j, k, l); 280 } 281 return "#xpointer(image-range(" + xpath + ",[" + i + "," + j + "],[" + k + "," + l + "]))"; 282 } 283 284 public native String filter(String xPointerFilter, ImageElement image, String xpath, int i, int j, int k, int l) /*-{ 285 if(xPointerFilter && top[xPointerFilter]) { 286 return top[xPointerFilter](image, xpath, i, j, k, l); 287 } 288 }-*/; 289 290 public Point[] filterAnnotation(Point topLeft, Point bottomRight) { 291 if (pointerAdapter == null) { 292 return new Point[] { topLeft, bottomRight }; 293 } 294 String result = filterPoint(pointerAdapter, topLeft.getX(), topLeft.getY(), bottomRight.getX(), 295 bottomRight.getY()); 296 if (result.equals("")) { 297 return null; 298 } 299 String[] points = result.split(":"); 300 return new Point[] { new Point(points[0]), new Point(points[1]) }; 301 } 302 303 private native String filterPoint(String pointerAdapter, int x, int y, int x2, int y2) /*-{ 304 if(pointerAdapter && top[pointerAdapter]) { 305 return top[pointerAdapter](x, y, x2, y2); 306 } 307 }-*/; 308 309 public static void updateAnnotation(boolean forceDecorate) { 310 CURRENT_INSTANCE.updateAnnotations(forceDecorate); 311 } 312 313 public static void updateAnnotation() { 314 updateAnnotation(false); 315 } 316 317 public void updateAnnotations(boolean forceDecorate) { 318 annotatedDocument.update(forceDecorate); 319 } 320 321 public native void setAnnotationDecoratorFunction(String annotationDecoratorFunction) /*-{ 322 top[annotationDecoratorFunction] = @org.nuxeo.ecm.platform.annotations.gwt.client.controler.AnnotationController::updateAnnotation(Z); 323 }-*/; 324 325 public void setNewAnnotationPopup(NewAnnotationPopup popup) { 326 newAnnotationPopup = popup; 327 } 328 329 public NewAnnotationPopup getNewAnnotationPopup() { 330 return newAnnotationPopup; 331 } 332 333 public void hideAnnotations() { 334 annotatedDocument.hideAnnotations(); 335 disablePopupListeners(); 336 } 337 338 public void disablePopupListeners() { 339 for (AnnotationPopupEventListener listener : annotationPopupListeners) { 340 listener.disable(); 341 } 342 } 343 344 public void showAnnotations() { 345 annotatedDocument.showAnnotations(); 346 enablePopupListeners(); 347 } 348 349 public void enablePopupListeners() { 350 for (AnnotationPopupEventListener listener : annotationPopupListeners) { 351 listener.enable(); 352 } 353 } 354 355 public void registerAnnotationPopupListener(AnnotationPopupEventListener listener) { 356 annotationPopupListeners.add(listener); 357 } 358 359 public void removeAnnotationPopupListener(AnnotationPopupEventListener listener) { 360 annotationPopupListeners.remove(listener); 361 } 362 363 public String getDecorateClassName() { 364 if (annotatedDocument.isAnnotationsVisible()) { 365 return AnnotationConstant.DECORATE_CLASS_NAME; 366 } else { 367 return AnnotationConstant.DECORATE_NOT_CLASS_NAME; 368 } 369 } 370 371 public native void deleteAnnotation(int index) /*-{ 372 top['deleteAnnotation'](index); 373 }-*/; 374 375 @SuppressWarnings("unused") 376 private void deleteAnnotationOnFrame(int index) { 377 annoteaClient.deleteAnnotation(Window.Location.getHref(), model.getAnnotations().get(index)); 378 } 379 380 public native boolean isAnnotationsVisible() /*-{ 381 if (typeof top['annotationsShown'] != "undefined") { 382 return top['annotationsShown']; 383 } else { 384 return true; 385 } 386 }-*/; 387 388 public void removeSelectedTextDecoration() { 389 annotatedDocument.removeSelectedTextDecoration(getNewAnnotation()); 390 } 391 392}