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}