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