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.model;
023
024import java.util.ArrayList;
025import java.util.List;
026
027import com.allen_sauer.gwt.log.client.Log;
028
029/**
030 * @author <a href="mailto:troger@nuxeo.com">Thomas Roger</a>
031 */
032public class TilingModel {
033
034    public enum TilingModelEvent {
035        MOVE_EVENT, TILING_INFO_UPDATED
036    };
037
038    private TilingInfo tilingInfo;
039
040    private int totalWidth;
041
042    private int totalHeight;
043
044    private int viewAreaTop;
045
046    private int viewAreaLeft;
047
048    private int viewAreaWidth;
049
050    private int viewAreaHeight;
051
052    private int maxLeft;
053
054    private int maxTop;
055
056    // we won't zoom out below this zoom
057    private double defaultZoom;
058
059    // used to recenter the area after zooming in / out
060    private int oldXToCenterOn = 0;
061
062    private int oldYToCenterOn = 0;
063
064    private double oldZoom = 1;
065
066    private List<TilingModelListener> listeners = new ArrayList<TilingModelListener>();
067
068    private TilingInfoCallback tilingInfoCallback = new TilingInfoCallback() {
069        public void tilingInfoUpdated() {
070            updateModel();
071
072            // compute new center
073            int x = (int) Math.round(oldXToCenterOn * getCurrentZoom() / oldZoom);
074            int y = (int) Math.round(oldYToCenterOn * getCurrentZoom() / oldZoom);
075            // center
076            Log.debug("center on: x=" + x + ", y=" + y);
077            centerOn(x, y, false);
078
079            // fire the event
080            fireEvent(TilingModelEvent.TILING_INFO_UPDATED);
081        }
082    };
083
084    public TilingModel(TilingInfo ti, int viewAreaWidth, int viewAreaHeight, double defaultZoom) {
085        tilingInfo = ti;
086        this.defaultZoom = defaultZoom;
087        this.viewAreaWidth = viewAreaWidth;
088        this.viewAreaHeight = viewAreaHeight;
089        viewAreaTop = viewAreaLeft = 0;
090        updateModel();
091    }
092
093    public void notifyListeners() {
094        fireEvent(TilingModelEvent.TILING_INFO_UPDATED);
095    }
096
097    private void updateModel() {
098        totalWidth = (int) Math.round(tilingInfo.getOriginalImageWidth() * getCurrentZoom());
099        totalHeight = (int) Math.round(tilingInfo.getOriginalImageHeight() * getCurrentZoom());
100
101        maxLeft = totalWidth - viewAreaWidth;
102        maxTop = totalHeight - viewAreaHeight;
103    }
104
105    public void move(int x, int y, boolean fireEvent) {
106        viewAreaLeft += x;
107        viewAreaTop += y;
108        ensureCorrectValues();
109        if (fireEvent) {
110            fireEvent(TilingModelEvent.MOVE_EVENT);
111        }
112    }
113
114    public void move(int x, int y) {
115        move(x, y, true);
116    }
117
118    public void resetView() {
119        viewAreaLeft = viewAreaTop = 0;
120    }
121
122    public void setLocation(int left, int top, boolean fireEvent) {
123        viewAreaLeft = left;
124        viewAreaTop = top;
125        ensureCorrectValues();
126        if (fireEvent) {
127            fireEvent(TilingModelEvent.MOVE_EVENT);
128        }
129    }
130
131    public void setLocation(int left, int top) {
132        setLocation(left, top, true);
133    }
134
135    public void centerOn(int x, int y, boolean fireEvent) {
136        viewAreaLeft = x - viewAreaWidth / 2;
137        viewAreaTop = y - viewAreaHeight / 2;
138        Log.debug("viewAreaLeft= " + viewAreaLeft + " viewAreaTop= " + viewAreaTop);
139        ensureCorrectValues();
140        if (fireEvent) {
141            fireEvent(TilingModelEvent.MOVE_EVENT);
142        }
143    }
144
145    public void centerOn(int x, int y) {
146        centerOn(x, y, true);
147    }
148
149    private void ensureCorrectValues() {
150        if (viewAreaLeft > maxLeft) {
151            viewAreaLeft = maxLeft;
152        }
153        if (viewAreaTop > maxTop) {
154            viewAreaTop = maxTop;
155        }
156        if (viewAreaLeft < 0) {
157            viewAreaLeft = 0;
158        }
159        if (viewAreaTop < 0) {
160            viewAreaTop = 0;
161        }
162    }
163
164    public void zoomIn() {
165        tilingInfo.setMaxTiles(tilingInfo.getMaxTiles() * 2);
166        saveOldCoord();
167        tilingInfo.updateTilingInfo(tilingInfoCallback);
168    }
169
170    public void zoomOut() {
171        if (getCurrentZoom() <= defaultZoom) {
172            return;
173        }
174        tilingInfo.setMaxTiles(tilingInfo.getMaxTiles() / 2);
175        saveOldCoord();
176        tilingInfo.updateTilingInfo(tilingInfoCallback);
177    }
178
179    private void saveOldCoord() {
180        oldZoom = getCurrentZoom();
181        oldXToCenterOn = viewAreaLeft + viewAreaWidth / 2;
182        oldYToCenterOn = viewAreaTop + viewAreaHeight / 2;
183    }
184
185    public void addListener(TilingModelListener listener) {
186        listeners.add(listener);
187    }
188
189    public void removeListener(TilingModelListener listener) {
190        listeners.remove(listener);
191    }
192
193    private void fireEvent(TilingModelEvent event) {
194        for (TilingModelListener l : listeners) {
195            l.onModelChange(event, this);
196        }
197    }
198
199    /**
200     * @return the totalWidth.
201     */
202    public int getTotalWidth() {
203        return totalWidth;
204    }
205
206    /**
207     * @return the totalHeight.
208     */
209    public int getTotalHeight() {
210        return totalHeight;
211    }
212
213    /**
214     * @return the viewAreaTop.
215     */
216    public int getViewAreaTop() {
217        return viewAreaTop;
218    }
219
220    /**
221     * @return the viewAreaLeft.
222     */
223    public int getViewAreaLeft() {
224        return viewAreaLeft;
225    }
226
227    /**
228     * @return the viewAreaWidth.
229     */
230    public int getViewAreaWidth() {
231        return viewAreaWidth;
232    }
233
234    /**
235     * @return the viewAreaHeight.
236     */
237    public int getViewAreaHeight() {
238        return viewAreaHeight;
239    }
240
241    public int getTileWidth() {
242        return tilingInfo.getTileWidth();
243    }
244
245    public int getTileHeight() {
246        return tilingInfo.getTileHeight();
247    }
248
249    public int getWidthInTiles() {
250        return tilingInfo.getNbXTiles();
251    }
252
253    public int getHeightInTiles() {
254        return tilingInfo.getNbYTiles();
255    }
256
257    public String getBaseUrl() {
258        return tilingInfo.getBaseUrl();
259    }
260
261    public double getCurrentZoom() {
262        return tilingInfo.getZoom();
263    }
264
265    public long getLastModificationDate() {
266        return tilingInfo.getLastModificationDate();
267    }
268
269}