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 *     troger
018 *
019 * $Id$
020 */
021
022package org.nuxeo.ecm.platform.pictures.tiles.gwt.client.view;
023
024import org.nuxeo.ecm.platform.pictures.tiles.gwt.client.model.TilingInfo;
025import org.nuxeo.ecm.platform.pictures.tiles.gwt.client.model.TilingModel;
026import org.nuxeo.ecm.platform.pictures.tiles.gwt.client.model.TilingModelListener;
027import org.nuxeo.ecm.platform.pictures.tiles.gwt.client.model.TilingModel.TilingModelEvent;
028import org.nuxeo.ecm.platform.pictures.tiles.gwt.client.util.Point;
029import org.nuxeo.ecm.platform.pictures.tiles.gwt.client.util.Rectangle;
030
031import com.allen_sauer.gwt.log.client.Log;
032import com.google.gwt.user.client.DOM;
033import com.google.gwt.user.client.Event;
034import com.google.gwt.user.client.ui.AbsolutePanel;
035import com.google.gwt.user.client.ui.Anchor;
036import com.google.gwt.user.client.ui.FocusPanel;
037import com.google.gwt.user.client.ui.Image;
038import com.google.gwt.user.client.ui.MouseListenerAdapter;
039import com.google.gwt.user.client.ui.SimplePanel;
040import com.google.gwt.user.client.ui.Widget;
041
042/**
043 * @author <a href="mailto:troger@nuxeo.com">Thomas Roger</a>
044 */
045public class TilingPreviewControllerPanel extends FocusPanel implements TilingModelListener {
046
047    private static final String DEFAULT_CLASS_NAME = "tilingPreviewControllerPanel";
048
049    private class ControllerMouseListener extends MouseListenerAdapter {
050
051        boolean mouseDown = false;
052
053        int x, y;
054
055        @Override
056        public void onMouseDown(Widget sender, int x, int y) {
057            mouseDown = true;
058            this.x = x;
059            this.y = y;
060            centerArea(x, y);
061
062            DOM.setStyleAttribute(imagesPanel.getElement(), "cursor", "move");
063            cancelEvent(Event.getCurrentEvent());
064        }
065
066        @Override
067        public void onMouseMove(Widget sender, int x, int y) {
068            if (mouseDown) {
069                moveArea(x - this.x, y - this.y);
070                this.x = x;
071                this.y = y;
072            }
073            cancelEvent(Event.getCurrentEvent());
074        }
075
076        @Override
077        public void onMouseUp(Widget sender, int x, int y) {
078            if (mouseDown) {
079                mouseDown = false;
080                moveArea(x - this.x, y - this.y);
081                updateModel();
082                DOM.setStyleAttribute(imagesPanel.getElement(), "cursor", "default");
083                cancelEvent(Event.getCurrentEvent());
084            }
085        }
086
087        @Override
088        public void onMouseLeave(Widget sender) {
089            onMouseUp(sender, x, y);
090            cancelEvent(Event.getCurrentEvent());
091        }
092
093    }
094
095    private class ControllerBlockAllEventsListener extends MouseListenerAdapter {
096
097        @Override
098        public void onMouseDown(Widget sender, int x, int y) {
099            cancelEvent(Event.getCurrentEvent());
100        }
101
102        @Override
103        public void onMouseEnter(Widget sender) {
104            cancelEvent(Event.getCurrentEvent());
105        }
106
107        @Override
108        public void onMouseLeave(Widget sender) {
109            cancelEvent(Event.getCurrentEvent());
110        }
111
112        @Override
113        public void onMouseMove(Widget sender, int x, int y) {
114            cancelEvent(Event.getCurrentEvent());
115        }
116
117        @Override
118        public void onMouseUp(Widget sender, int x, int y) {
119            cancelEvent(Event.getCurrentEvent());
120        }
121
122    }
123
124    private final TilingInfo sourceTilingInfo;
125
126    private final TilingModel model;
127
128    private int totalWidth;
129
130    private int totalHeight;
131
132    private AbsolutePanel imagesPanel;
133
134    private double factor = 1;
135
136    private SelectedArea selectedArea;
137
138    private final Anchor anchor = new Anchor();
139
140    private final SimplePanel area = new SimplePanel();
141
142    private final FocusPanel anchorContainer = new FocusPanel(anchor);
143
144    private final ControllerMouseListener controllerMouseListener = new ControllerMouseListener();
145
146    private final ControllerBlockAllEventsListener blockAllEventsListener = new ControllerBlockAllEventsListener();
147
148    public TilingPreviewControllerPanel(TilingInfo sourceTilingInfo, TilingModel model) {
149        this.sourceTilingInfo = sourceTilingInfo;
150        this.model = model;
151        model.addListener(this);
152
153        imagesPanel = new AbsolutePanel();
154        totalWidth = (int) Math.round(sourceTilingInfo.getOriginalImageWidth() * sourceTilingInfo.getZoom());
155        totalHeight = (int) Math.round(sourceTilingInfo.getOriginalImageHeight() * sourceTilingInfo.getZoom());
156        imagesPanel.setPixelSize(totalWidth, totalHeight);
157        setWidget(imagesPanel);
158
159        // load the images corresponding to the first TilingInfo retrieved
160        loadImages();
161
162        createSelectedArea();
163
164        addMouseListener(controllerMouseListener);
165
166        setStyleName(DEFAULT_CLASS_NAME);
167    }
168
169    private void loadImages() {
170
171        int heightInTiles = sourceTilingInfo.getNbYTiles();
172        int widthInTiles = sourceTilingInfo.getNbXTiles();
173
174        for (int y = 0; y < heightInTiles; ++y) {
175            for (int x = 0; x < widthInTiles; ++x) {
176                String imageUrl = sourceTilingInfo.getBaseUrl() + "?x=" + x + "&y=" + y;
177                imageUrl += "&date=" + sourceTilingInfo.getLastModificationDate();
178
179                Image image = new Image(imageUrl) {
180                    @Override
181                    public void onBrowserEvent(Event event) {
182                        // cancelEvent(event);
183                    }
184                };
185
186                int imageX = x * sourceTilingInfo.getTileWidth();
187                int imageY = y * sourceTilingInfo.getTileHeight();
188                imagesPanel.add(image, imageX, imageY);
189            }
190        }
191        imagesPanel.add(area);
192    }
193
194    private void createSelectedArea() {
195        selectedArea = new SelectedArea(totalWidth, totalHeight, imagesPanel);
196    }
197
198    private void reloadSelectedArea() {
199        factor = model.getCurrentZoom() / sourceTilingInfo.getZoom();
200
201        int left = (int) Math.round(model.getViewAreaLeft() / factor);
202        int top = (int) Math.round(model.getViewAreaTop() / factor);
203        int w = (int) Math.round(model.getViewAreaWidth() / factor);
204        int h = (int) Math.round(model.getViewAreaHeight() / factor);
205
206        int maxW = (int) Math.round(sourceTilingInfo.getOriginalImageWidth() * sourceTilingInfo.getZoom());
207        int maxH = (int) Math.round(sourceTilingInfo.getOriginalImageHeight() * sourceTilingInfo.getZoom());
208        Log.debug("w: " + w + " maxW: " + maxW + " h: " + h + " maxH: " + maxH);
209        if (w > maxW) {
210            w = maxW;
211        }
212        if (h > maxH) {
213            h = maxH;
214        }
215
216        Point topLeft = new Point(left, top);
217        Rectangle area = new Rectangle(topLeft, w, h);
218
219        if (sourceTilingInfo.getZoom() == model.getCurrentZoom()) {
220            imagesPanel.remove(anchorContainer);
221            removeMouseListener(controllerMouseListener);
222            addMouseListener(blockAllEventsListener);
223        } else {
224            imagesPanel.add(anchorContainer);
225            removeMouseListener(blockAllEventsListener);
226            addMouseListener(controllerMouseListener);
227        }
228        selectedArea.changeArea(area);
229    }
230
231    private void updateModel() {
232        Point center = selectedArea.getCurrentArea().getCenter();
233        int x = (int) Math.round(center.getX() * factor);
234        int y = (int) Math.round(center.getY() * factor);
235        model.centerOn(x, y);
236        selectedArea.putArea();
237    }
238
239    private void moveArea(int dx, int dy) {
240        selectedArea.move(dx, dy);
241    }
242
243    private void centerArea(int x, int y) {
244        selectedArea.centerOn(x, y);
245    }
246
247    private static void cancelEvent(Event event) {
248        event.preventDefault();
249        event.cancelBubble(true);
250    }
251
252    public void onModelChange(TilingModelEvent event, TilingModel model) {
253        switch (event) {
254        case TILING_INFO_UPDATED:
255            reloadSelectedArea();
256            selectedArea.putArea();
257            break;
258        }
259    }
260
261}