/*
* This is part of Geomajas, a GIS framework, http://www.geomajas.org/.
*
* Copyright 2008-2015 Geosparc nv, http://www.geosparc.com/, Belgium.
*
* The program is available in open source according to the GNU Affero
* General Public License. All contributions in this program are covered
* by the Geomajas Contributors License Agreement. For full licensing
* details, see LICENSE.txt in the project root.
*/
package org.geomajas.gwt2.client.widget.map;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.geomajas.gwt.client.event.PointerTouchCancelEvent;
import org.geomajas.gwt.client.event.PointerTouchCancelHandler;
import org.geomajas.gwt.client.event.PointerTouchEndEvent;
import org.geomajas.gwt.client.event.PointerTouchEndHandler;
import org.geomajas.gwt.client.event.PointerTouchMoveEvent;
import org.geomajas.gwt.client.event.PointerTouchMoveHandler;
import org.geomajas.gwt.client.event.PointerTouchStartEvent;
import org.geomajas.gwt.client.event.PointerTouchStartHandler;
import org.geomajas.gwt2.client.GeomajasImpl;
import org.geomajas.gwt2.client.gfx.CanvasContainer;
import org.geomajas.gwt2.client.gfx.CanvasContainerImpl;
import org.geomajas.gwt2.client.gfx.TransformableWidgetContainer;
import org.geomajas.gwt2.client.gfx.TransformableWidgetContainerImpl;
import org.geomajas.gwt2.client.gfx.VectorContainer;
import org.geomajas.gwt2.client.gfx.VectorGroup;
import org.geomajas.gwt2.client.map.MapPresenterImpl.MapWidget;
import org.geomajas.gwt2.client.map.render.dom.container.HtmlContainer;
import org.geomajas.gwt2.client.map.render.dom.container.HtmlGroup;
import org.geomajas.gwt2.client.widget.control.watermark.Watermark;
import org.vaadin.gwtgraphics.client.DrawingArea;
import org.vaadin.gwtgraphics.client.Group;
import org.vaadin.gwtgraphics.client.Transformable;
import com.google.gwt.dom.client.Style;
import com.google.gwt.event.dom.client.DoubleClickEvent;
import com.google.gwt.event.dom.client.DoubleClickHandler;
import com.google.gwt.event.dom.client.GestureChangeEvent;
import com.google.gwt.event.dom.client.GestureChangeHandler;
import com.google.gwt.event.dom.client.GestureEndEvent;
import com.google.gwt.event.dom.client.GestureEndHandler;
import com.google.gwt.event.dom.client.GestureStartEvent;
import com.google.gwt.event.dom.client.GestureStartHandler;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.dom.client.MouseMoveEvent;
import com.google.gwt.event.dom.client.MouseMoveHandler;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.dom.client.MouseUpHandler;
import com.google.gwt.event.dom.client.MouseWheelEvent;
import com.google.gwt.event.dom.client.MouseWheelHandler;
import com.google.gwt.event.dom.client.TouchCancelEvent;
import com.google.gwt.event.dom.client.TouchCancelHandler;
import com.google.gwt.event.dom.client.TouchEndEvent;
import com.google.gwt.event.dom.client.TouchEndHandler;
import com.google.gwt.event.dom.client.TouchMoveEvent;
import com.google.gwt.event.dom.client.TouchMoveHandler;
import com.google.gwt.event.dom.client.TouchStartEvent;
import com.google.gwt.event.dom.client.TouchStartHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.RequiresResize;
import com.google.gwt.user.client.ui.Widget;
/**
* <p> Implementation of the MapWidget interface. It represents the MVP 'view' of the map's presenter (aka
* MapPresenter). </p> <p> This widget is able to render all required objects that the MapPresenter supports, and does
* this in the following order: <ol> <li>Raster layers & rasterized vector layers.</li> <li>Vector layers (SVG/VML)</li>
* <li>All vectorcontainers</li> <li>All map gadgets</li> </ol> </p>
*
* @author Pieter De Graef
* @author Jan De Moerloose
*/
public final class MapWidgetImpl extends AbsolutePanel implements MapWidget {
// Container for raster layers or rasterized layers:
private HtmlGroup layerHtmlContainer;
// Container for vector layers (SVG/VML):
private VectorGroup layerVectorContainer;
// Parent container for all SVG/VML:
private DrawingArea drawingArea;
// Parent container for all canvases:
private AbsolutePanel canvasPanel;
// Parent container for all transformable widget containers
private FlowPanel widgetPanel;
// List of all screen containers:
private List<VectorContainer> screenContainers = new ArrayList<VectorContainer>();
// List of all world containers:
private List<VectorContainer> worldContainers = new ArrayList<VectorContainer>();
// List of all world canvas containers:
private List<CanvasContainer> worldCanvases = new ArrayList<CanvasContainer>();
// List of all widget containers:
private List<TransformableWidgetContainer> widgetContainers = new ArrayList<TransformableWidgetContainer>();
// List of all world transformables (canvas + vector):
private List<Transformable> worldTransformables = new ArrayList<Transformable>();
private final List<Widget> widgets = new ArrayList<Widget>();
// ------------------------------------------------------------------------
// Constructors:
// ------------------------------------------------------------------------
public MapWidgetImpl() {
super();
// Attach an HtmlContainer inside the clipping area (used for rendering layers):
layerHtmlContainer = new HtmlGroup();
layerHtmlContainer.asWidget().getElement().getStyle().setZIndex(0);
add(layerHtmlContainer, 0, 0);
// Add a panel to hold the canvases (this should come before vectors or it catches all events !)
canvasPanel = new AbsolutePanel();
add(canvasPanel, 0, 0);
// Attach a DrawingArea inside the clipping area (used for vector rendering):
drawingArea = new DrawingArea(100, 100);
add(drawingArea, 0, 0);
// First child within the vector drawing area is a group for the map to render it's non-HTML layers:
layerVectorContainer = new VectorGroup();
drawingArea.add(layerVectorContainer);
widgetPanel = new FlowPanel();
add(widgetPanel, 0, 0);
// Firefox and Chrome allow for DnD of images. This default behavior is not wanted.
addMouseDownHandler(new MouseDownHandler() {
public void onMouseDown(MouseDownEvent event) {
event.preventDefault();
}
});
addMouseMoveHandler(new MouseMoveHandler() {
public void onMouseMove(MouseMoveEvent event) {
event.preventDefault();
}
});
// We don't want scrolling on the page and zooming at the same time.
// TODO: make this optional. When no zoom on scroll is used, scrolling the page should be possible.
addMouseWheelHandler(new MouseWheelHandler() {
public void onMouseWheel(MouseWheelEvent event) {
event.preventDefault();
}
});
// Apply the correct style name:
MapWidgetResource resource = GeomajasImpl.getClientBundleFactory().createMapWidgetResource();
resource.css().ensureInjected();
this.setStyleName(resource.css().mapBackground());
}
@Override
public Widget asWidget() {
return this;
}
@Override
public HtmlContainer getMapHtmlContainer() {
return layerHtmlContainer;
}
@Override
public List<Transformable> getWorldTransformables() {
return worldTransformables;
}
@Override
public VectorContainer getNewScreenContainer() {
VectorGroup container = new VectorGroup();
drawingArea.add(container);
screenContainers.add(container);
return container;
}
@Override
public VectorContainer getNewWorldContainer() {
VectorGroup container = new VectorGroup();
drawingArea.add(container);
worldContainers.add(container);
worldTransformables.add(container);
return container;
}
@Override
public CanvasContainer getNewWorldCanvas() {
CanvasContainer container = new CanvasContainerImpl(getWidth(), getHeight());
canvasPanel.add(container, 0, 0);
worldCanvases.add(container);
worldTransformables.add(container);
return container;
}
@Override
public TransformableWidgetContainer getNewWorldWidgetContainer() {
TransformableWidgetContainerImpl container = new TransformableWidgetContainerImpl();
widgetPanel.add(container);
widgetContainers.add(container);
worldTransformables.add(container);
return container;
}
@Override
public boolean removeVectorContainer(VectorContainer container) {
if (container instanceof Group) {
if (worldContainers.contains(container)) {
drawingArea.remove((Group) container);
worldContainers.remove(container);
worldTransformables.remove(container);
return true;
} else if (screenContainers.contains(container)) {
drawingArea.remove((Group) container);
screenContainers.remove(container);
return true;
}
}
return false;
}
@Override
public boolean removeWorldWidgetContainer(TransformableWidgetContainer container) {
if (worldContainers.contains(container)) {
widgetPanel.remove(container);
widgetContainers.remove(container);
worldTransformables.remove(container);
return true;
}
return false;
}
@Override
public boolean bringToFront(VectorContainer container) {
if (container instanceof Group) {
if (worldContainers.contains(container)) {
drawingArea.bringToFront((Group) container);
return true;
} else if (screenContainers.contains(container)) {
drawingArea.bringToFront((Group) container);
return true;
}
}
return false;
}
@Override
public HasWidgets getWidgetContainer() {
return new HasWidgets() {
@Override
public void add(Widget w) {
w.getElement().getStyle().setPosition(Style.Position.ABSOLUTE);
MapWidgetImpl.this.add(w);
if (!(w instanceof Watermark)) {
widgets.add(w);
}
}
@Override
public void clear() {
while (widgets.size() > 0) {
remove(widgets.get(0));
}
}
@Override
public Iterator<Widget> iterator() {
return widgets.iterator();
}
@Override
public boolean remove(Widget w) {
widgets.remove(w);
return MapWidgetImpl.this.remove(w);
}
};
}
@Override
public void onResize() {
for (Widget child : getChildren()) {
if (child instanceof RequiresResize) {
((RequiresResize) child).onResize();
}
}
}
// ------------------------------------------------------------------------
// Overriding resize methods:
// ------------------------------------------------------------------------
public void setPixelSize(int width, int height) {
layerHtmlContainer.setPixelSize(width, height);
drawingArea.setWidth(width);
drawingArea.setHeight(height);
canvasPanel.setPixelSize(width, height);
for (CanvasContainer container : worldCanvases) {
container.setPixelSize(width, height);
}
super.setPixelSize(width, height);
}
public void setSize(String width, String height) {
layerHtmlContainer.setSize(width, height);
drawingArea.setWidth(width);
drawingArea.setHeight(height);
canvasPanel.setWidth(width);
canvasPanel.setHeight(height);
for (CanvasContainer container : worldCanvases) {
container.setPixelSize(drawingArea.getWidth(), drawingArea.getHeight());
}
super.setSize(width, height);
}
public int getWidth() {
return getOffsetWidth();
}
public void setWidth(int width) {
setWidth(width + "px");
}
public void setWidth(String width) {
layerHtmlContainer.setWidth(width);
drawingArea.setWidth(width);
canvasPanel.setWidth(width);
for (CanvasContainer container : worldCanvases) {
container.setPixelSize(drawingArea.getWidth(), drawingArea.getHeight());
}
super.setWidth(width);
}
public int getHeight() {
return getOffsetHeight();
}
public void setHeight(int height) {
setHeight(height + "px");
}
public void setHeight(String height) {
layerHtmlContainer.setHeight(height);
drawingArea.setHeight(height);
canvasPanel.setHeight(height);
for (CanvasContainer container : worldCanvases) {
container.setPixelSize(drawingArea.getWidth(), drawingArea.getHeight());
}
super.setHeight(height);
}
// ------------------------------------------------------------------------
// Add mouse event catch methods:
// ------------------------------------------------------------------------
public HandlerRegistration addMouseDownHandler(MouseDownHandler handler) {
return addDomHandler(handler, MouseDownEvent.getType());
}
public HandlerRegistration addMouseUpHandler(MouseUpHandler handler) {
return addDomHandler(handler, MouseUpEvent.getType());
}
public HandlerRegistration addMouseOutHandler(MouseOutHandler handler) {
return addDomHandler(handler, MouseOutEvent.getType());
}
public HandlerRegistration addMouseOverHandler(MouseOverHandler handler) {
return addDomHandler(handler, MouseOverEvent.getType());
}
public HandlerRegistration addMouseMoveHandler(MouseMoveHandler handler) {
return addDomHandler(handler, MouseMoveEvent.getType());
}
public HandlerRegistration addMouseWheelHandler(MouseWheelHandler handler) {
return addDomHandler(handler, MouseWheelEvent.getType());
}
public HandlerRegistration addDoubleClickHandler(DoubleClickHandler handler) {
return addDomHandler(handler, DoubleClickEvent.getType());
}
// ------------------------------------------------------------------------
// Touch event catch methods:
// ------------------------------------------------------------------------
@Override
public HandlerRegistration addTouchStartHandler(TouchStartHandler handler) {
return addDomHandler(handler, TouchStartEvent.getType());
}
@Override
public HandlerRegistration addTouchEndHandler(TouchEndHandler handler) {
return addDomHandler(handler, TouchEndEvent.getType());
}
@Override
public HandlerRegistration addTouchCancelHandler(TouchCancelHandler handler) {
return addDomHandler(handler, TouchCancelEvent.getType());
}
@Override
public HandlerRegistration addTouchMoveHandler(TouchMoveHandler handler) {
return addDomHandler(handler, TouchMoveEvent.getType());
}
// ------------------------------------------------------------------------
// Gesture event catch methods:
// ------------------------------------------------------------------------
@Override
public HandlerRegistration addGestureStartHandler(GestureStartHandler handler) {
return addDomHandler(handler, GestureStartEvent.getType());
}
@Override
public HandlerRegistration addGestureChangeHandler(GestureChangeHandler handler) {
return addDomHandler(handler, GestureChangeEvent.getType());
}
@Override
public HandlerRegistration addGestureEndHandler(GestureEndHandler handler) {
return addDomHandler(handler, GestureEndEvent.getType());
}
@Override
public HandlerRegistration addPointerTouchStartHandler(PointerTouchStartHandler handler) {
return addDomHandler(handler, PointerTouchStartEvent.getType());
}
@Override
public HandlerRegistration addPointerTouchEndHandler(PointerTouchEndHandler handler) {
return addDomHandler(handler, PointerTouchEndEvent.getType());
}
@Override
public HandlerRegistration addPointerTouchMoveHandler(PointerTouchMoveHandler handler) {
return addDomHandler(handler, PointerTouchMoveEvent.getType());
}
@Override
public HandlerRegistration addPointerTouchCancelHandler(PointerTouchCancelHandler handler) {
return addDomHandler(handler, PointerTouchCancelEvent.getType());
}
}