es.ucm.fdi.clover.view.BaseView.java Source code

Java tutorial

Introduction

Here is the source code for es.ucm.fdi.clover.view.BaseView.java

Source

/**
 * AC - A source-code copy detector
 *
 *     For more information please visit:  http://github.com/manuel-freire/ac
 *
 * ****************************************************************************
 *
 * This file is part of AC, version 2.0
 *
 * AC is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the
 * Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * AC is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with AC.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * BaseView.java
 *
 * Created on May 6, 2006, 8:55 PM
 * Original Author: Manuel Freire (manuel.freire@uam.es)
 */

package es.ucm.fdi.clover.view;

import es.ucm.fdi.clover.gui.CloverSave;
import es.ucm.fdi.clover.model.ViewGraph;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.Point2D;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Map;
import javax.swing.AbstractAction;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.KeyStroke;

import javax.swing.ToolTipManager;
import org.jgraph.JGraph;

import es.ucm.fdi.clover.model.BaseGraph;
import es.ucm.fdi.clover.model.Edge;
import org.jdom2.Element;
import org.jgraph.event.GraphModelEvent;
import org.jgraph.event.GraphModelListener;
import org.jgraph.graph.CellView;
import org.jgraph.graph.DefaultEdge;
import org.jgraph.graph.GraphCell;
import org.jgraph.graph.GraphConstants;

/**
 * This is what you get to see and interact with in Clover. All UI stuff is to
 * be found here. This is the base for the 'component' hierarchy, so you only
 * get the basic stuff at this level. No clusters, therefore no aura.
 *
 * @author mfreire
 */
public class BaseView extends JGraph implements Printable {

    public static final String scaleProperty = "scaleProperty";
    public static final String zoomProperty = "zoomProperty";

    /** what we are currently seeing (should be synced via animator with base) */
    protected ViewGraph viewGraph;

    /** the Animator (in charge of syncing viewGraph and actual base) */
    protected Animator animator;

    /** the AnimationPlan currently under execution, if any */
    private AnimationPlan currentPlan;

    /** allows a 'layout zoom' to be used; propagated on all view<->layout operations */
    private double layoutZoom;

    /** used to avoid saving the positions after the layout-zoom */
    private double oldLayoutZoom = -1;

    /**
     * Creates a new instance of BaseView
     */
    public BaseView() {
        this(new ViewGraph(new BaseGraph()));
    }

    public BaseView(ViewGraph viewGraph) {
        super(viewGraph.getModelAdapter());
        layoutZoom = 1.0;

        this.viewGraph = viewGraph;
        setBase(viewGraph.getBase());

        this.setAntiAliased(true);
        this.setSelectionEnabled(true);
        this.setEditable(false);
        this.setDisconnectable(false);
        this.setEdgeLabelsMovable(false);
        this.setPortsVisible(false);
        this.setMoveable(true);
        this.setBendable(false);

        ToolTipManager.sharedInstance().registerComponent(this);
        ToolTipManager.sharedInstance().setLightWeightPopupEnabled(false);
        ToolTipManager.sharedInstance().setDismissDelay(1000 * 30);

        if (getParent() instanceof JViewport) {
            ((JScrollPane) getParent().getParent()).setWheelScrollingEnabled(false);
        }

        this.getInputMap().put(KeyStroke.getKeyStroke('a'), "debug-lcache");
        this.getActionMap().put("debug-lcache", new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
                animator.dumpLayoutCache(getBase());
            }
        });

        this.getInputMap().put(KeyStroke.getKeyStroke('p'), "debug-plan");
        this.getActionMap().put("debug-plan", new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
                AnimationPlan p = getCurrentPlan();
                if (p != null) {
                    System.err.println("Current plan: " + p.getDescription());
                    System.err.println("isRunning: " + p.isRunning());
                } else {
                    System.err.println("No plan.");
                }
            }
        });

        this.getInputMap().put(KeyStroke.getKeyStroke('s'), "save-lcache");
        this.getActionMap().put("save-lcache", new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
                ArrayList<ClusterView> al = new ArrayList<ClusterView>();
                al.add((ClusterView) e.getSource());
                try {
                    CloverSave.save(al, new File("/tmp/f"));
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        });

        // allows very simple zooming
        this.addMouseWheelListener(new MouseWheelListener() {
            public void mouseWheelMoved(MouseWheelEvent e) {
                int i = e.getWheelRotation();

                double delta = 0.05;
                double scale = getScale();
                double next;
                if (scale < 1.0 || (layoutZoom == 1.0 && i > 0)) {
                    // scale-unzoom
                    next = (i < 0) ? Math.min(scale + delta, 1.0) : Math.max(delta, scale - delta);

                    if (i < 0)
                        setRelativeCenter(e.getPoint(), next / scale);

                    setScale(next);
                    firePropertyChange(scaleProperty, scale, next);
                } else {
                    // do the 'position zoom' thing here
                    next = (i < 0) ? Math.min(layoutZoom * (1.0 + delta), 4.0)
                            : Math.max(layoutZoom * (1.0 - delta), 1.0);

                    if (i < 0)
                        setRelativeCenter(e.getPoint(), next / layoutZoom);

                    setLayoutZoom(next);
                    firePropertyChange(zoomProperty, scale, next);
                }

                // System.err.println("Layout-zoom: "+layoutZoom);
                // System.err.println("Scale: "+getScale());               
            }
        });

        this.getModel().addGraphModelListener(new GraphModelListener() {
            public void graphChanged(GraphModelEvent e) {
                // zoom changes are undistinguishable from user changes - distinguish them
                if (oldLayoutZoom != layoutZoom) {
                    oldLayoutZoom = layoutZoom;
                    return;
                }

                // only resyncs if there is no plan executing
                if (currentPlan == null) {
                    animator.resyncFromEvent(e);
                }
            }
        });
    }

    public BaseGraph getBase() {
        return viewGraph.getBase();
    }

    public ViewGraph getViewGraph() {
        return viewGraph;
    }

    public void setRelativeCenter(Point2D p, double zoomDiff) {
        JViewport jvp = (JViewport) getParent();
        Point2D tl = jvp.getViewPosition();
        Point next = new Point();
        p.setLocation(Math.min(getWidth(), p.getX()), Math.min(getHeight(), p.getY()));
        next.setLocation(tl.getX() + (p.getX() - tl.getX()) * (1 - (1 / zoomDiff)),
                tl.getY() + (p.getY() - tl.getY()) * (1 - (1 / zoomDiff)));
        next.setLocation(Math.max(0, next.getX()), Math.max(0, next.getY()));

        Dimension nd = jvp.getViewSize();
        // next dims
        nd.setSize(nd.getWidth() / zoomDiff, nd.getHeight() / zoomDiff);
        // required dims
        Dimension rd = getPreferredSize();

        if (!next.equals(tl) && (nd.getHeight() < rd.getHeight() || nd.getWidth() < rd.getWidth())) {
            //System.err.println("zoom-recenter: \nreq\t"+rd+"\nnxt\t"+nd);
            jvp.setViewPosition(next);
        }
        //        else {
        //            System.err.println("NO-recenter: \nreq\t"+rd+"\nnxt\t"+nd);
        //        }
    }

    /**
     * If the parent is a viewport, shifts the viewport's window so that
     * the selected 'view coordinates' appear centered; note that corrections
     * for scale must be made since 'view coords' != 'view rect coords'
     *
     * FIXME: also, if any part of the results would be offscreen, 
     * errors will crop up; stupid java libraries!.
     */
    public void setCenter(Point2D desiredCenter) {
        JViewport jvp = (JViewport) getParent();

        Dimension nd = jvp.getViewSize();
        Dimension rd = getPreferredSize();
        if (nd.width > rd.width && nd.height > rd.height) {
            return;
        }

        // current view position: portion of the graph being represented
        Dimension viewSize = jvp.getExtentSize();
        Point2D currentCenter = jvp.getViewPosition();
        currentCenter.setLocation(currentCenter.getX() + viewSize.getWidth() / 2,
                currentCenter.getY() + viewSize.getHeight() / 2);

        double dx = -currentCenter.getX() + desiredCenter.getX();
        double dy = -currentCenter.getY() + desiredCenter.getY();

        // calculate desired rectangle
        Point p = new Point(jvp.getViewPosition());
        p.setLocation(Math.max(0, p.getX() + dx), Math.max(0, p.getY() + dy));
        // scroll
        jvp.setViewPosition(p);
    }

    public void setBase(BaseGraph base) {
        if (viewGraph.getBase() != base) {
            viewGraph.setBase(base);
        }
        setModel(viewGraph.getModelAdapter());
        if (animator == null) {
            animator = new Animator(this);
        } else {
            animator.setView(this);
        }
    }

    /**
     * Returns the label for a given vertex or edge cell
     */
    public String convertValueToString(Object cell) {
        String label = null;

        if (cell instanceof CellView) {
            cell = ((CellView) cell).getCell();
        }

        if (cell instanceof GraphCell && cell != null) {
            Map attribs = ((GraphCell) cell).getAttributes();
            label = (String) attribs.get(ViewGraph.LABEL);
            if (label == null) {
                Object o = GraphConstants.getValue(attribs);
                label = (cell instanceof DefaultEdge) ? viewGraph.getEdgeLabel((Edge) o)
                        : viewGraph.getVertexLabel(o);
            }
            if (label != null && label.length() == 0) {
                return null;
            }
        }

        return (label != null) ? label : super.convertValueToString(cell);
    }

    /**
     * Returns a tool-tip for the current location, which can be over
     * either a vertex or an edge cell.
     */
    public String getToolTipText(MouseEvent event) {
        Object cell = getFirstCellForLocation(event.getX(), event.getY());
        String tooltip = null;

        if (cell instanceof GraphCell && cell != null) {
            Map attribs = ((GraphCell) cell).getAttributes();
            tooltip = (String) attribs.get(ViewGraph.TOOLTIP);
            if (tooltip == null) {
                Object o = GraphConstants.getValue(attribs);
                tooltip = (cell instanceof DefaultEdge) ? viewGraph.getEdgeToolTip((Edge) o)
                        : viewGraph.getVertexToolTip(o);
            }
            if (tooltip != null && tooltip.length() == 0) {
                tooltip = null;
            }
        }

        //        else {
        //            tooltip = ""+event.getPoint().getX()+","+event.getPoint().getY();
        //        }

        return tooltip;
    }

    public Animator getAnimator() {
        return animator;
    }

    public void setAnimator(Animator animator) {
        if (this.animator != null) {
            getBase().removeStructureChangeListener(this.animator);
        }
        this.animator = animator;
        animator.setView(this);
    }

    public AnimationPlan getCurrentPlan() {
        return currentPlan;
    }

    public void setCurrentPlan(AnimationPlan currentPlan) {
        this.currentPlan = currentPlan;
    }

    public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
        throw new UnsupportedOperationException("Printing not yet supported");
    }

    /**
     * Behaviour for large scales is to place nodes further apart
     */
    public void setLayoutZoom(double val) {
        animator.getManager().setNodes(this);
        this.layoutZoom = val;
        // FIXME: animate?
        animator.getManager().applyChanges(this);
    }

    public double getLayoutZoom() {
        return layoutZoom;
    }

    public void save(Element e) {
        e.setAttribute("zoom", "" + (layoutZoom > 1 ? layoutZoom : getScale()));

        String topCorner = "" + getVisibleRect().getX() + "," + getVisibleRect().getY();
        e.setAttribute("topCorner", topCorner);
    }

    public void restore(Element e) {
        float f = Float.parseFloat(e.getAttributeValue("zoom"));
        if (f > 1)
            setLayoutZoom(f);
        else
            setScale(f);

        String[] p = e.getAttributeValue("topCorner").split(",");

        //      FIXME: cannot be called unless parent has been set...        
        //        ((JViewport)getParent()).setViewPosition(
        //                new Point((int)Float.parseFloat(p[0]), (int)Float.parseFloat(p[1])));        
    }
}