/*
* Copyright 2012 Rui Afonso
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.googlecode.gwt.charts.client;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.RequiresResize;
import com.google.gwt.user.client.ui.Widget;
import com.googlecode.gwt.charts.client.event.Event;
import com.googlecode.gwt.charts.client.event.EventHandler;
import com.googlecode.gwt.charts.client.event.HandlerRef;
import com.googlecode.gwt.charts.client.options.Options;
import java.util.HashMap;
/**
* This class is used as a widget wrapper for all chart types.
* Supports automatically resizing via {@link RequiresResize}, which means that all parents
* must implement {@link com.google.gwt.user.client.ui.ProvidesResize} for this to work.
* It also supports attach/detach funcionality.
*
* @param <T> the chart options type
*/
public abstract class ChartWidget<T extends Options> extends Widget implements RequiresResize {
protected ChartObject chartObject;
private DataSource data;
private Options options;
private HashMap<HandlerRef, EventHandler> eventMap;
private boolean unloaded;
private boolean pending;
/**
* Creates a new ChartWidget.
*/
public ChartWidget() {
super();
Element chartDiv = DOM.createDiv();
chartObject = createChartObject(chartDiv);
setElement(chartDiv);
eventMap = new HashMap<HandlerRef, EventHandler>();
}
/**
* Clears the chart, and releases all of its allocated resources.
*/
public void clearChart() {
chartObject.clearChart();
}
/**
* Draws the visualization on the page. Behind the scenes this can be fetching a graphic from a server or creating
* the graphic on the page using the linked visualization code. You should call this method every time the data or
* options change.
*
* @param data a {@link DataTable} or {@link DataView} holding the data to use to draw the chart.
* @see <a href="http://developers.google.com/chart/interactive/docs/reference.html#visdraw">draw API reference</a>
*/
public void draw(DataSource data) {
draw(data, null);
}
/**
* Draws the visualization on the page. Behind the scenes this can be fetching a graphic from a server or creating
* the graphic on the page using the linked visualization code. You should call this method every time the data or
* options change.
*
* @param data a {@link DataTable} or {@link DataView} holding the data to use to draw the chart.
* @param options A map of name/value pairs of custom options.
* @see <a href="http://developers.google.com/chart/interactive/docs/reference.html#visdraw">draw API reference</a>
*/
public void draw(DataSource data, T options) {
this.data = data;
this.options = options;
redraw();
}
/**
* Fires an event to all listeners.
*
* @param event the event object to fire
*/
public void fireEvent(Event event) {
chartObject.trigger(event.getEventName(), event.getProperties());
}
@Override
public void onResize() {
redraw();
}
/**
* Redraws the chart with last used data and options.
*/
public void redraw() {
if (pending) {
return;
}
pending = true;
// Double deferred command because of layout issues
Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() {
@Override
public void execute() {
Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() {
@Override
public void execute() {
redrawNow();
}
});
}
});
}
/**
* Removes all existing handlers from this chart.
*/
public void removeAllHandlers() {
eventMap.clear();
chartObject.removeAllListeners();
}
/**
* Removes a single handler matching the given handler reference.
*
* @param handlerRef an handler reference
*/
public void removeHandler(HandlerRef handlerRef) {
eventMap.remove(handlerRef);
chartObject.removeListener(handlerRef);
}
/**
* Call this method to register to receive events fired by a visualization hosted on your page. Note that this will
* not work for visualizations embedded in a gadget.
*
* @param <H>
* @param handler the function to call when the event is fired
* @return the new handler reference. Can be used for removing by calling {@link #removeHandler(HandlerRef)}.
*/
protected final <H extends EventHandler> HandlerRef addHandler(H handler) {
HandlerRef handlerRef = chartObject.addListener(handler.getEventName(), handler);
eventMap.put(handlerRef, handler);
return handlerRef;
}
protected abstract ChartObject createChartObject(Element parent);
@Override
protected void onLoad() {
if (!unloaded) {
return;
}
unloaded = false;
recreate();
redraw();
}
@Override
protected void onUnload() {
this.unloaded = true;
}
protected void recreate() {
chartObject = createChartObject(getElement());
for (EventHandler eventHandler : eventMap.values()) {
chartObject.addListener(eventHandler.getEventName(), eventHandler);
}
}
protected void redrawNow() {
if (data != null) {
chartObject.draw(data, options);
}
pending = false;
}
}