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