/**
* Copyright (c) 2009, iPlant Collaborative, Texas Advanced Computing Center This software is licensed
* under the CC-GNU GPL version 2.0 or later. License: http://creativecommons.org/licenses/GPL/2.0/
*/
package org.iplantc.phyloviewer.client.tree.viewer;
import org.iplantc.phyloviewer.client.events.DocumentChangeEvent;
import org.iplantc.phyloviewer.client.events.DocumentChangeHandler;
import org.iplantc.phyloviewer.client.events.HasDocument;
import org.iplantc.phyloviewer.client.events.HasNodeSelectionHandlers;
import org.iplantc.phyloviewer.client.events.NodeSelectionEvent;
import org.iplantc.phyloviewer.client.events.NodeSelectionHandler;
import org.iplantc.phyloviewer.client.events.RenderEvent;
import org.iplantc.phyloviewer.client.events.RenderHandler;
import org.iplantc.phyloviewer.client.tree.viewer.render.HasRenderPreferences;
import org.iplantc.phyloviewer.shared.layout.ILayoutData;
import org.iplantc.phyloviewer.shared.math.Box2D;
import org.iplantc.phyloviewer.shared.model.IDocument;
import org.iplantc.phyloviewer.shared.model.INode;
import org.iplantc.phyloviewer.shared.model.ITree;
import org.iplantc.phyloviewer.shared.render.Camera;
import org.iplantc.phyloviewer.shared.render.RenderPreferences;
import org.iplantc.phyloviewer.shared.render.style.IStyleMap;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.event.shared.SimpleEventBus;
import com.google.gwt.user.client.ui.FocusPanel;
import com.google.gwt.user.client.ui.RequiresResize;
public abstract class View extends FocusPanel implements RequiresResize, HasDocument,
HasNodeSelectionHandlers, HasRenderPreferences
{
public enum LayoutType
{
LAYOUT_TYPE_CLADOGRAM, LAYOUT_TYPE_CIRCULAR
}
private Camera camera;
private IDocument document;
private boolean renderRequestPending = false;
LayoutType layoutType;
private EventBus eventBus = new SimpleEventBus();
private RenderPreferences renderPreferences = new RenderPreferences();
/** A NodeSelectionHandler that re-fires selection events with this view as the source */
protected NodeSelectionHandler refireHandler = new NodeSelectionHandler()
{
@Override
public void onNodeSelection(NodeSelectionEvent event)
{
getEventBus().fireEventFromSource(new NodeSelectionEvent(event.getSelectedNodes()),
View.this);
}
};
public View()
{
this.initEventListeners();
}
@Override
public IDocument getDocument()
{
return document;
}
@Override
public void setDocument(IDocument document)
{
this.document = document;
if(eventBus != null)
{
eventBus.fireEventFromSource(new DocumentChangeEvent(document), this);
}
}
/**
* Set the style map.
* @param styleMap
*/
public void setStyleMap(IStyleMap styleMap)
{
IDocument document = getDocument();
if(document != null)
{
document.setStyleMap(styleMap);
this.requestRender();
}
}
@Override
public HandlerRegistration addDocumentChangeHandler(DocumentChangeHandler handler)
{
return eventBus.addHandlerToSource(DocumentChangeEvent.TYPE, this, handler);
}
@Override
public HandlerRegistration addSelectionHandler(NodeSelectionHandler handler)
{
return eventBus.addHandlerToSource(NodeSelectionEvent.TYPE, this, handler);
}
public ITree getTree()
{
return this.getDocument() != null ? this.getDocument().getTree() : null;
}
public Camera getCamera()
{
return camera;
}
public void setCamera(Camera camera)
{
this.camera = camera;
}
public ILayoutData getLayout()
{
return document != null ? document.getLayout() : null;
}
public void zoomToFit()
{
if(null != this.getTree())
{
zoomToFitSubtree(getTree().getRootNode());
}
}
public void zoomToFitSubtree(final INode subtree)
{
if(subtree != null)
{
this.zoomToFitSubtree(subtree.getId());
}
}
public void zoomToFitSubtree(final int nodeId)
{
ILayoutData layout = this.getLayout();
if(null != layout)
{
// No need to check for layout data. As of now, layout data always is present with node data.
// If this changes, logic for handling remote data should be in the document.
Box2D boundingBox = layout.getBoundingBox(nodeId);
this.zoomToBoundingBox(boundingBox);
}
}
public void zoomToBoundingBox(Box2D boundingBox)
{
if(null != this.getCamera())
{
getCamera().zoomToBoundingBox(boundingBox);
this.dispatch(new RenderEvent());
}
}
public abstract void resize(int width, int height);
public abstract void render();
/**
* This gets called by TreeWidget before every render, so it must return quickly
*/
public abstract boolean isReady();
public void requestRender()
{
if(!this.renderRequestPending)
{
this.renderRequestPending = true;
Scheduler.get().scheduleDeferred(new ScheduledCommand()
{
@Override
public void execute()
{
if(View.this.isReady())
{
View.this.render();
View.this.renderRequestPending = false;
}
else
{
Scheduler.get().scheduleDeferred(this);
}
}
});
}
}
public abstract String exportImageURL();
public void setEventBus(EventBus eventBus)
{
this.eventBus = eventBus;
this.initEventListeners();
}
public EventBus getEventBus()
{
return eventBus;
}
@Override
public void onResize()
{
resize(getParent().getOffsetWidth(), getParent().getOffsetHeight());
this.requestRender();
}
@Override
public RenderPreferences getRenderPreferences()
{
return renderPreferences;
}
@Override
public void setRenderPreferences(RenderPreferences rp)
{
this.renderPreferences = rp;
}
public LayoutType getLayoutType()
{
return layoutType;
}
protected void setLayoutType(LayoutType type)
{
this.layoutType = type;
}
protected void dispatch(GwtEvent<?> event)
{
if(eventBus != null)
{
eventBus.fireEventFromSource(event, this);
}
}
protected void initEventListeners()
{
if(eventBus != null)
{
eventBus.addHandler(RenderEvent.TYPE, new RenderHandler()
{
@Override
public void onRender(RenderEvent event)
{
requestRender();
}
});
}
}
public void pan(double xAmount, double yAmount)
{
getCamera().pan(xAmount, yAmount);
dispatch(new RenderEvent());
}
public void zoom(double amount)
{
getCamera().zoom(amount);
dispatch(new RenderEvent());
}
}