com.googlecode.blaisemath.graphics.core.DelegatingPointSetGraphic.java Source code

Java tutorial

Introduction

Here is the source code for com.googlecode.blaisemath.graphics.core.DelegatingPointSetGraphic.java

Source

/**
 * DelegatingPointSetGraphic.java
 * Created Jan 22, 2011
 */
package com.googlecode.blaisemath.graphics.core;

/*
 * #%L
 * BlaiseGraphics
 * --
 * Copyright (C) 2009 - 2015 Elisha Peterson
 * --
 * 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.
 * #L%
 */

import com.google.common.base.Functions;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.googlecode.blaisemath.annotation.InvokedFromThread;
import static com.googlecode.blaisemath.graphics.core.LabeledPointGraphic.LABEL_RENDERER_PROP;
import static com.googlecode.blaisemath.graphics.core.PrimitiveGraphicSupport.RENDERER_PROP;
import com.googlecode.blaisemath.style.ObjectStyler;
import com.googlecode.blaisemath.style.Renderer;
import com.googlecode.blaisemath.style.Styles;
import com.googlecode.blaisemath.util.AnchoredText;
import com.googlecode.blaisemath.util.coordinate.CoordinateChangeEvent;
import com.googlecode.blaisemath.util.coordinate.CoordinateListener;
import com.googlecode.blaisemath.util.coordinate.CoordinateManager;
import com.googlecode.blaisemath.util.swing.BSwingUtilities;
import java.awt.geom.Point2D;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.swing.JPopupMenu;

/**
 * Manages a collection of points that are maintained as separate {@link Graphic}s,
 * and therefore fully customizable. Point locations are handled by a {@link CoordinateManager},
 * which allows their locations to be safely modified from other threads.
 *
 * @param <S> the type of object being displayed
 * @param <G> type of canvas to render to
 *
 * @see BasicPointSetGraphic
 *
 * @author Elisha Peterson
 */
public class DelegatingPointSetGraphic<S, G> extends GraphicComposite<G> {

    private static final int NODE_CACHE_SIZE = 20000;

    /** Key for flag allowing individual points to be selected */
    public static final String POINT_SELECTION_ENABLED = "point-selection-enabled";

    /** Graphic objects for individual points */
    protected final Map<S, DelegatingPrimitiveGraphic<S, Point2D, G>> points = Maps.newHashMap();
    /** Whether points can be dragged */
    protected boolean dragEnabled = false;

    /** Manages locations of points */
    protected CoordinateManager<S, Point2D> manager;
    /** Selects styles for graphics */
    @Nonnull
    protected ObjectStyler<S> styler = ObjectStyler.create();
    /** Selects renderer for points */
    protected Renderer<Point2D, G> renderer;
    /** Renderer for point labels */
    protected Renderer<AnchoredText, G> textRenderer;

    /** Indicates points are being updated */
    protected boolean updatingPoint = false;
    /** Responds to coordinate update events */
    private final CoordinateListener coordListener;

    /**
     * Construct with no points
     */
    public DelegatingPointSetGraphic() {
        this(null, null);
    }

    /**
     * Construct with no points
     * @param renderer draws points
     * @param labelRenderer draws labels
     */
    public DelegatingPointSetGraphic(@Nullable Renderer<Point2D, G> renderer,
            @Nullable Renderer<AnchoredText, G> labelRenderer) {
        this(CoordinateManager.<S, Point2D>create(NODE_CACHE_SIZE), renderer, labelRenderer);
    }

    /**
     * Construct with source objects and locations as a map
     * @param crdManager manages point locations
     * @param renderer used for drawing the points
     * @param labelRenderer draws labels
     */
    public DelegatingPointSetGraphic(CoordinateManager<S, Point2D> crdManager,
            @Nullable Renderer<Point2D, G> renderer, @Nullable Renderer<AnchoredText, G> labelRenderer) {
        setRenderer(renderer);
        setLabelRenderer(labelRenderer);

        styler.setStyleConstant(Styles.DEFAULT_POINT_STYLE);
        styler.setTipDelegate(Functions.toStringFunction());

        coordListener = new CoordinateListener() {
            @Override
            @InvokedFromThread("unknown")
            public void coordinatesChanged(final CoordinateChangeEvent evt) {
                BSwingUtilities.invokeOnEventDispatchThread(new Runnable() {
                    @Override
                    public void run() {
                        updatePointGraphics(evt.getAdded(), evt.getRemoved());
                    }
                });
            }
        };

        setCoordinateManager(crdManager);
    }

    //<editor-fold defaultstate="collapsed" desc="EVENT HANDLERS">
    //
    // EVENT HANDLERS
    //

    private void updatePointGraphics(Map<S, Point2D> added, Set<S> removed) {
        List<Graphic<G>> addMe = Lists.newArrayList();
        if (added != null) {
            for (Entry<S, Point2D> en : added.entrySet()) {
                S src = en.getKey();
                DelegatingPrimitiveGraphic<S, Point2D, G> dpg = points.get(src);
                if (dpg == null) {
                    LabeledPointGraphic<S, G> lpg = new LabeledPointGraphic<S, G>(en.getKey(), en.getValue(),
                            styler);
                    lpg.setRenderer(renderer);
                    lpg.setLabelRenderer(textRenderer);
                    lpg.setDragEnabled(dragEnabled);
                    lpg.setSelectionEnabled(isPointSelectionEnabled());
                    points.put(src, lpg);
                    addMe.add(lpg);
                } else {
                    // this should not result in manager changing
                    updatingPoint = true;
                    dpg.setPrimitive(en.getValue());
                    updatingPoint = false;
                }
            }
        }
        Set<DelegatingPrimitiveGraphic<S, Point2D, G>> removeMe = Sets.newHashSet();
        if (removed != null) {
            for (S s : removed) {
                removeMe.add(points.get(s));
                points.remove(s);
            }
        }
        replaceGraphics(removeMe, addMe);
    }

    @Override
    public void graphicChanged(Graphic<G> source) {
        if (!updatingPoint && source instanceof LabeledPointGraphic) {
            LabeledPointGraphic<S, G> dpg = (LabeledPointGraphic<S, G>) source;
            manager.put(dpg.getSourceObject(), dpg.getPrimitive());
        }
        // do not propagate events unless flag is false
        if (!updatingPoint) {
            super.graphicChanged(source);
        }
    }

    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc="PROPERTY PATTERNS">
    //
    // PROPERTY PATTERNS
    //

    /**
     * Returns true if individual points can be selected.
     * @return true if points can be selected
     */
    public boolean isPointSelectionEnabled() {
        return styleHints.getBoolean(POINT_SELECTION_ENABLED, false);
    }

    public void setPointSelectionEnabled(boolean val) {
        if (isPointSelectionEnabled() != val) {
            styleHints.put(POINT_SELECTION_ENABLED, val);
            for (DelegatingPrimitiveGraphic<S, Point2D, G> dpg : points.values()) {
                dpg.setSelectionEnabled(val);
            }
        }
    }

    /**
     * Manager responsible for tracking point locations
     * @return manager
     */
    public CoordinateManager<S, Point2D> getCoordinateManager() {
        return manager;
    }

    /**
     * Set manager responsible for tracking point locations
     * @param mgr manager
     */
    public final void setCoordinateManager(CoordinateManager<S, Point2D> mgr) {
        if (this.manager != checkNotNull(mgr)) {
            if (this.manager != null) {
                this.manager.removeCoordinateListener(coordListener);
            }
            this.manager = mgr;
            this.manager.addCoordinateListener(coordListener);
            updatePointGraphics(mgr.getActiveLocationCopy(), Collections.EMPTY_SET);
        }
    }

    /**
     * Returns object used to style points
     * @return styler object styler
     */
    public ObjectStyler<S> getStyler() {
        return styler;
    }

    /**
     * Sets object used to style points
     * @param styler object styler
     */
    public void setStyler(ObjectStyler<S> styler) {
        if (this.styler != checkNotNull(styler)) {
            this.styler = styler;
            fireGraphicChanged();
        }
    }

    @Nullable
    public Renderer<Point2D, G> getRenderer() {
        return renderer;
    }

    public final void setRenderer(@Nullable Renderer<Point2D, G> renderer) {
        if (this.renderer != renderer) {
            Object old = this.renderer;
            this.renderer = renderer;
            updatingPoint = true;
            for (DelegatingPrimitiveGraphic<S, Point2D, G> dpg : points.values()) {
                dpg.setRenderer(renderer);
            }
            updatingPoint = false;
            fireGraphicChanged();
            pcs.firePropertyChange(RENDERER_PROP, old, renderer);
        }
    }

    @Nullable
    public Renderer<AnchoredText, G> getLabelRenderer() {
        return textRenderer;
    }

    public final void setLabelRenderer(@Nullable Renderer<AnchoredText, G> renderer) {
        if (this.textRenderer != renderer) {
            Object old = this.renderer;
            this.textRenderer = renderer;
            fireGraphicChanged();
            pcs.firePropertyChange(LABEL_RENDERER_PROP, old, renderer);
        }
    }

    public boolean isDragEnabled() {
        return dragEnabled;
    }

    public void setDragEnabled(boolean val) {
        if (this.dragEnabled != val) {
            this.dragEnabled = val;
            for (DelegatingPrimitiveGraphic<S, Point2D, G> dpg : points.values()) {
                dpg.setDragEnabled(val);
            }
        }
    }

    /**
     * Return source objects.
     * @return source objects
     */
    public Set<S> getObjects() {
        return manager.getActive();
    }

    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc="MUTATORS">

    /**
     * Adds objects to the graphic
     * @param obj objects to put
     */
    public final void addObjects(Map<S, Point2D> obj) {
        manager.putAll(obj);
    }

    //</editor-fold>

    @Nullable
    public DelegatingPrimitiveGraphic<S, Point2D, G> getPointGraphic(S source) {
        return points.get(source);
    }

    @Override
    public void initContextMenu(JPopupMenu menu, Graphic src, Point2D point, Object focus, Set selection) {
        // provide additional info for context menu
        Graphic gfc = graphicAt(point);
        super.initContextMenu(menu, this, point,
                gfc instanceof DelegatingPrimitiveGraphic ? ((DelegatingPrimitiveGraphic) gfc).getSourceObject()
                        : focus,
                selection);
    }

}