Package org.thechiselgroup.choosel.visualization_component.chart.client

Source Code of org.thechiselgroup.choosel.visualization_component.chart.client.ChartViewContentDisplay

/*******************************************************************************
* Copyright 2009, 2010 Lars Grammel
*
* 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 org.thechiselgroup.choosel.visualization_component.chart.client;

import org.thechiselgroup.choosel.core.client.util.collections.Delta;
import org.thechiselgroup.choosel.core.client.util.collections.LightweightCollection;
import org.thechiselgroup.choosel.core.client.visualization.model.AbstractViewContentDisplay;
import org.thechiselgroup.choosel.core.client.visualization.model.Slot;
import org.thechiselgroup.choosel.core.client.visualization.model.VisualItem;
import org.thechiselgroup.choosel.core.client.visualization.model.VisualItem.Status;
import org.thechiselgroup.choosel.core.client.visualization.model.VisualItem.Subset;
import org.thechiselgroup.choosel.core.client.visualization.model.VisualItemInteraction;
import org.thechiselgroup.choosel.protovis.client.PV;
import org.thechiselgroup.choosel.protovis.client.PVEventHandler;
import org.thechiselgroup.choosel.protovis.client.PVMark;
import org.thechiselgroup.choosel.protovis.client.PVPanel;
import org.thechiselgroup.choosel.protovis.client.ProtovisWidget;
import org.thechiselgroup.choosel.protovis.client.jsutil.JsArgs;
import org.thechiselgroup.choosel.protovis.client.jsutil.JsArrayGeneric;
import org.thechiselgroup.choosel.protovis.client.jsutil.JsUtils;

import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Widget;

/**
* An abstract ViewContentDisplay class which any Protovis chart's specific
* ViewContentDisplay can extend.
*
* @author Bradley Blashko
* @author Lars Grammel
*/
public abstract class ChartViewContentDisplay extends
        AbstractViewContentDisplay {

    private static final String CSS_CLASS_CHOOSEL_CHART_WIDGET = "Choosel-Chart-Widget";

    // TODO wrapper for jsarraygeneric that implements java.util.List
    protected JsArrayGeneric<VisualItem> visualItemsJsArray = JsUtils
            .createJsArrayGeneric();

    protected String[] eventTypes = { PV.Event.CLICK, PV.Event.MOUSEDOWN,
            PV.Event.MOUSEMOVE, PV.Event.MOUSEOUT, PV.Event.MOUSEOVER,
            PV.Event.MOUSEUP };

    private PVEventHandler handler = new PVEventHandler() {
        @Override
        public void onEvent(Event e, String pvEventType, JsArgs args) {
            ChartViewContentDisplay.this.onEvent(e, pvEventType, args);
        }
    };

    /**
     * Flags status that chart widget is rendering. While rendering, events are
     * discarded.
     */
    protected boolean isRendering;

    protected ProtovisWidget chartWidget;

    protected int width;

    protected int height;

    public void addVisualItem(VisualItem visualItem) {
        visualItemsJsArray.push(visualItem);
    }

    /**
     * Called after the rendering is finished. Subclasses can override this
     * method to clear temporary objects that were constructed for the rendering
     * process.
     */
    protected void afterRender() {
    }

    /**
     * Is called before the chart is rendered. Subclasses can override this
     * method to recalculate values that are used for all resource item specific
     * calls from Protovis.
     */
    protected void beforeRender() {
    }

    /**
     * Builds the visualization. <code>buildChart</code> is only called if there
     * are actual data items that can be rendered ( jsVisualItems.length >= 1 ).
     */
    protected abstract void buildChart();

    @Override
    public final Widget createWidget() {
        chartWidget = new ProtovisWidget();
        chartWidget.addStyleName(CSS_CLASS_CHOOSEL_CHART_WIDGET);
        return chartWidget;
    }

    protected PVPanel getChart() {
        return chartWidget.getPVPanel();
    }

    public VisualItem getVisualItem(int index) {
        assert visualItemsJsArray != null;
        assert 0 <= index;
        assert index < visualItemsJsArray.length();

        return visualItemsJsArray.get(index);
    }

    // TODO refactoring: introduce view item list that offers this functionality
    public boolean hasVisualItemsWithPartialSubset(Subset subset) {
        for (int i = 0; i < visualItemsJsArray.length(); i++) {
            VisualItem visualItem = visualItemsJsArray.get(i);
            if (visualItem.isStatus(subset, Status.PARTIAL)) {
                return true;
            }
        }
        return false;
    }

    protected void onEvent(Event e, String pvEventType, JsArgs args) {
        int index = args.<PVMark> getThis().index();
        getVisualItem(index).reportInteraction(new VisualItemInteraction(e));
    }

    protected abstract void registerEventHandler(String eventType,
            PVEventHandler handler);

    protected void registerEventHandlers() {
        for (String eventType : eventTypes) {
            registerEventHandler(eventType, handler);
        }
    }

    // TODO move into js array to java.util.List wrapper
    public void removeVisualItem(VisualItem visualItem) {
        int occurences = 0;
        for (int i = 0; i < visualItemsJsArray.length(); i++) {
            VisualItem itemFromArray = visualItemsJsArray.get(i);
            if (itemFromArray == visualItem) {
                occurences++;
            } else if (occurences > 0) {
                visualItemsJsArray.set(i - occurences, itemFromArray);
            }
        }
        visualItemsJsArray.setLength(visualItemsJsArray.length() - occurences);
    }

    @Override
    public void setSize(int width, int height) {
        if (width == this.width && height == this.height) {
            return;
        }

        this.width = width;
        this.height = height;

        super.setSize(width, height);

        /*
         * TODO we could use updateChart(false) here to improve the performance.
         * This would require several changes in the chart implementation,
         * though.
         */
        updateChart(true);
    }

    // XXX rename
    protected void setUpChartForRendering(boolean structuralChange) {
        if (structuralChange) {
            chartWidget.initPVPanel();
            if (visualItemsJsArray.length() > 0) {
                buildChart();
                registerEventHandlers();
            }
        }
    }

    /**
     * A method that listens for any updates on any resource items relevant to
     * the chart. Chart only will get rendered or updated (depending on the
     * situation) once no matter how many resource items are being affected.
     */
    @Override
    public void update(Delta<VisualItem> delta,
            LightweightCollection<Slot> changedSlots) {

        if (!isAttached()) {
            return;
        }

        for (VisualItem visualItem : delta.getAddedElements()) {
            addVisualItem(visualItem);
        }

        for (VisualItem visualItem : delta.getRemovedElements()) {
            removeVisualItem(visualItem);
        }

        /*
         * PERFORMANCE only rebuild the chart SVG DOM elements when structure
         * changes (i.e. resource items are added or removed), otherwise just
         * update their attributes.
         *
         * TODO check under which circumstances a rebuild is required if
         * structure changes or if rendering is sufficient
         *
         * TODO changing slots requires a rebuild because it affects the scales
         * and rulers - look for a better solution
         */
        updateChart(!delta.getAddedElements().isEmpty()
                || !delta.getRemovedElements().isEmpty()
                || !changedSlots.isEmpty());
    }

    /**
     * Updates the visualization.
     *
     * @param structuralChange
     *            If <code>true</code>, the current chart is abandoned and a new
     *            Protovis panel is used and a new visualization is build from
     *            scratch and rendered. If <code>false</code>, the current
     *            visualization is re-rendered and the chart structure will not
     *            change, just the attributes of the SVG elements are updated.
     */
    protected void updateChart(boolean structuralChange) {
        if (!isAttached()) {
            return; // cannot render yet
        }

        setUpChartForRendering(structuralChange);

        /*
         * XXX re-rendering with layout requires reset see
         * "http://groups.google.com/group/protovis/browse_thread/thread/b9032215a2f5ac25"
         *
         * TODO instead of isRendering flag, remove event listeners before
         * rendering starts and add them again after rendering is finished.
         */
        // XXX how often are event listeners assigned? are they removed?
        try {
            isRendering = true;
            beforeRender();
            getChart().render();
            afterRender(); // TODO move into finally block?
        } finally {
            isRendering = false;
        }
    }
}
TOP

Related Classes of org.thechiselgroup.choosel.visualization_component.chart.client.ChartViewContentDisplay

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.