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 java.util.HashMap;
023import java.util.Map;
024
025import org.nuxeo.ecm.platform.pictures.tiles.gwt.client.controller.TilingController;
026import org.nuxeo.ecm.platform.pictures.tiles.gwt.client.model.TilingModel;
027import org.nuxeo.ecm.platform.pictures.tiles.gwt.client.model.TilingModelListener;
028import org.nuxeo.ecm.platform.pictures.tiles.gwt.client.model.TilingModel.TilingModelEvent;
029import org.nuxeo.ecm.platform.pictures.tiles.gwt.client.util.Point;
030import org.nuxeo.ecm.platform.pictures.tiles.gwt.client.util.Rectangle;
031import org.nuxeo.ecm.platform.pictures.tiles.gwt.client.view.i18n.TranslationConstants;
032
033import com.allen_sauer.gwt.log.client.Log;
034import com.google.gwt.core.client.GWT;
035import com.google.gwt.user.client.Window;
036import com.google.gwt.user.client.ui.AbsolutePanel;
037import com.google.gwt.user.client.ui.Button;
038import com.google.gwt.user.client.ui.ClickListener;
039import com.google.gwt.user.client.ui.Composite;
040import com.google.gwt.user.client.ui.HorizontalPanel;
041import com.google.gwt.user.client.ui.Image;
042import com.google.gwt.user.client.ui.Widget;
043
044/**
045 * @author <a href="mailto:troger@nuxeo.com">Thomas Roger</a>
046 */
047public class TilingPreviewPanel extends Composite implements TilingModelListener {
048
049    private static final String DEFAULT_CLASS_NAME = "tilingPreviewPanel";
050
051    private AbsolutePanel tilingViewPanel;
052
053    private AbsolutePanel tilingView;
054
055    private final TilingController controller;
056
057    private final TilingModel model;
058
059    private final Map<String, Image> images = new HashMap<String, Image>();
060
061    public TilingPreviewPanel(TilingController tilingController, TilingModel tilingModel) {
062        controller = tilingController;
063        model = tilingModel;
064        model.addListener(this);
065
066        // create the panel to show the part of the image
067        tilingViewPanel = new AbsolutePanel();
068        tilingViewPanel.setPixelSize(model.getViewAreaWidth(), model.getViewAreaHeight());
069        tilingView = new AbsolutePanel();
070        tilingViewPanel.add(tilingView);
071
072        tilingViewPanel.addStyleName("tilingViewPanel");
073
074        AbsolutePanel rootPanel = new AbsolutePanel();
075        rootPanel.setPixelSize(model.getViewAreaWidth(), model.getViewAreaHeight());
076        rootPanel.addStyleName("tilingMasterContainer");
077        rootPanel.add(tilingViewPanel, 0, 0);
078
079        HorizontalPanel hPanel = createZoomButtons();
080        rootPanel.add(hPanel, 20, 20);
081
082        initWidget(rootPanel);
083
084        setStyleName(DEFAULT_CLASS_NAME);
085    }
086
087    private HorizontalPanel createZoomButtons() {
088        HorizontalPanel buttons = new HorizontalPanel();
089
090        TranslationConstants translationContants = GWT.create(TranslationConstants.class);
091
092        Button resetZoomButton = new Button();
093        resetZoomButton.addClickListener(new ClickListener() {
094            public void onClick(Widget arg0) {
095                Window.Location.assign(Window.Location.getHref());
096                decorate();
097            }
098        });
099        resetZoomButton.addStyleName("resetZoomButton");
100        resetZoomButton.setTitle(translationContants.zoom());
101        buttons.add(resetZoomButton);
102
103        Button zoomInButton = new Button();
104        zoomInButton.addClickListener(new ClickListener() {
105            public void onClick(Widget arg0) {
106                model.zoomIn();
107            }
108        });
109        zoomInButton.addStyleName("zoomInButton");
110        zoomInButton.setTitle(translationContants.zoomIn());
111        buttons.add(zoomInButton);
112
113        Button zoomOutButton = new Button();
114        zoomOutButton.addClickListener(new ClickListener() {
115            public void onClick(Widget arg0) {
116                model.zoomOut();
117            }
118        });
119        zoomOutButton.addStyleName("zoomOutButton");
120        zoomOutButton.setTitle(translationContants.zoomOut());
121        buttons.add(zoomOutButton);
122
123        return buttons;
124    }
125
126    public void onModelChange(TilingModelEvent event, TilingModel model) {
127        switch (event) {
128        case MOVE_EVENT:
129            // update the view (move)
130            renderView();
131            break;
132        case TILING_INFO_UPDATED:
133            // clean and reload the view
134            resetView();
135            renderView();
136            break;
137        }
138    }
139
140    private void renderView() {
141        int widthInTiles = model.getWidthInTiles();
142        int heightInTiles = model.getHeightInTiles();
143
144        int top = model.getViewAreaTop();
145        int left = model.getViewAreaLeft();
146        int width = model.getViewAreaWidth();
147        int height = model.getViewAreaHeight();
148
149        Rectangle viewableArea = new Rectangle(new Point(left, top), width, height);
150
151        tilingView.clear();
152        for (int y = 0; y < heightInTiles; ++y) {
153            for (int x = 0; x < widthInTiles; ++x) {
154                String imageUrl = model.getBaseUrl() + "?x=" + x + "&y=" + y;
155                imageUrl += "&date=" + model.getLastModificationDate();
156
157                int imageLeft = x * model.getTileWidth();
158                int imageTop = y * model.getTileHeight();
159                int imageRight = imageLeft + model.getTileWidth();
160                int imageBottom = imageTop + model.getTileHeight();
161
162                Point imageTopLeft = new Point(imageLeft, imageTop);
163                Point imageTopRight = new Point(imageRight, imageTop);
164                Point imageBottomLeft = new Point(imageLeft, imageBottom);
165                Point imageBottomRight = new Point(imageRight, imageBottom);
166
167                if (!viewableArea.containsAny(imageTopLeft, imageBottomLeft, imageTopRight, imageBottomRight)) {
168                    // Always draw the first image. It's used to display the
169                    // annotations
170                    if (!(x == 0 && y == 0)) {
171                        continue;
172                    }
173                }
174
175                Log.debug("Getting image: " + imageUrl);
176                Image image = getImage(imageUrl);
177
178                if (x == 0 && y == 0) {
179                    image.getElement().setId("annotationRootImage");
180                }
181
182                tilingView.add(image, imageLeft, imageTop);
183            }
184        }
185        tilingViewPanel.add(tilingView, -left, -top);
186
187        decorate();
188    }
189
190    private Image getImage(String imageUrl) {
191        Image image = images.get(imageUrl);
192        if (image == null) {
193            image = new Image(imageUrl);
194            images.put(imageUrl, image);
195        }
196        return image;
197    }
198
199    private void decorate() {
200        controller.updateAnnotationDecoration();
201    }
202
203    private void resetView() {
204        images.clear();
205        tilingView.clear();
206        tilingView.setPixelSize(model.getTotalWidth(), model.getTotalHeight());
207    }
208
209}