/** * $Id: mxCellStatePreview.java,v 1.2 2014/02/19 09:41:00 gaudenz Exp $ * Copyright (c) 2007, Gaudenz Alder */ package com.mxgraph.swing.view; import java.awt.AlphaComposite; import java.awt.Composite; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import com.mxgraph.canvas.mxGraphics2DCanvas; import com.mxgraph.model.mxGeometry; import com.mxgraph.model.mxIGraphModel; import com.mxgraph.swing.mxGraphComponent; import com.mxgraph.util.mxPoint; import com.mxgraph.util.mxRectangle; import com.mxgraph.util.mxUtils; import com.mxgraph.view.mxCellState; import com.mxgraph.view.mxGraph; /** * Represents the current state of a cell in a given graph view. */ public class mxCellStatePreview { /** * */ protected Map<mxCellState, mxPoint> deltas = new LinkedHashMap<mxCellState, mxPoint>(); /** * */ protected int count = 0; /** * */ protected mxGraphComponent graphComponent; /** * Specifies if cell states should be cloned or changed in-place. */ protected boolean cloned; /** * */ protected float opacity = 1; /** * */ protected List<mxCellState> cellStates; /** * Constructs a new state preview. The paint handler to invoke the paint * method must be installed elsewhere. */ public mxCellStatePreview(mxGraphComponent graphComponent, boolean cloned) { this.graphComponent = graphComponent; this.cloned = cloned; } /** * */ public boolean isCloned() { return cloned; } /** * */ public void setCloned(boolean value) { cloned = value; } /** * */ public boolean isEmpty() { return count == 0; } /** * */ public int getCount() { return count; } /** * */ public Map<mxCellState, mxPoint> getDeltas() { return deltas; } /** * */ public void setOpacity(float value) { opacity = value; } /** * */ public float getOpacity() { return opacity; } /** * */ public mxPoint moveState(mxCellState state, double dx, double dy) { return moveState(state, dx, dy, true, true); } /** * */ public mxPoint moveState(mxCellState state, double dx, double dy, boolean add, boolean includeEdges) { mxPoint delta = deltas.get(state); if (delta == null) { delta = new mxPoint(dx, dy); deltas.put(state, delta); count++; } else { if (add) { delta.setX(delta.getX() + dx); delta.setY(delta.getY() + dy); } else { delta.setX(dx); delta.setY(dy); } } if (includeEdges) { addEdges(state); } return delta; } /** * Returns a dirty rectangle to be repainted in mxGraphControl. */ public mxRectangle show() { mxGraph graph = graphComponent.getGraph(); mxIGraphModel model = graph.getModel(); // Stores a copy of the cell states List<mxCellState> previousStates = null; if (isCloned()) { previousStates = new LinkedList<mxCellState>(); Iterator<mxCellState> it = deltas.keySet().iterator(); while (it.hasNext()) { mxCellState state = it.next(); previousStates.addAll(snapshot(state)); } } // Translates the states in step Iterator<mxCellState> it = deltas.keySet().iterator(); while (it.hasNext()) { mxCellState state = it.next(); mxPoint delta = deltas.get(state); mxCellState parentState = graph.getView().getState( model.getParent(state.getCell())); translateState(parentState, state, delta.getX(), delta.getY()); } // Revalidates the states in step mxRectangle dirty = null; it = deltas.keySet().iterator(); while (it.hasNext()) { mxCellState state = it.next(); mxPoint delta = deltas.get(state); mxCellState parentState = graph.getView().getState( model.getParent(state.getCell())); mxRectangle tmp = revalidateState(parentState, state, delta.getX(), delta.getY()); if (dirty != null) { dirty.add(tmp); } else { dirty = tmp; } } // Takes a snapshot of the states for later drawing. If the states // are not cloned then this does nothing and just expects a repaint // of the dirty rectangle. if (previousStates != null) { cellStates = new LinkedList<mxCellState>(); it = deltas.keySet().iterator(); while (it.hasNext()) { mxCellState state = it.next(); cellStates.addAll(snapshot(state)); } // Restores the previous states restore(previousStates); } if (dirty != null) { dirty.grow(2); } return dirty; } /** * */ public void restore(List<mxCellState> snapshot) { mxGraph graph = graphComponent.getGraph(); Iterator<mxCellState> it = snapshot.iterator(); while (it.hasNext()) { mxCellState state = it.next(); mxCellState orig = graph.getView().getState(state.getCell()); if (orig != null && orig != state) { restoreState(orig, state); } } } /** * */ public void restoreState(mxCellState state, mxCellState from) { state.setLabelBounds(from.getLabelBounds()); state.setAbsolutePoints(from.getAbsolutePoints()); state.setOrigin(from.getOrigin()); state.setAbsoluteOffset(from.getAbsoluteOffset()); state.setBoundingBox(from.getBoundingBox()); state.setTerminalDistance(from.getTerminalDistance()); state.setSegments(from.getSegments()); state.setLength(from.getLength()); state.setX(from.getX()); state.setY(from.getY()); state.setWidth(from.getWidth()); state.setHeight(from.getHeight()); } /** * */ public List<mxCellState> snapshot(mxCellState state) { List<mxCellState> result = new LinkedList<mxCellState>(); if (state != null) { result.add((mxCellState) state.clone()); mxGraph graph = graphComponent.getGraph(); mxIGraphModel model = graph.getModel(); Object cell = state.getCell(); int childCount = model.getChildCount(cell); for (int i = 0; i < childCount; i++) { result.addAll(snapshot(graph.getView().getState( model.getChildAt(cell, i)))); } } return result; } /** * */ protected void translateState(mxCellState parentState, mxCellState state, double dx, double dy) { if (state != null) { mxGraph graph = graphComponent.getGraph(); mxIGraphModel model = graph.getModel(); Object cell = state.getCell(); if (model.isVertex(cell)) { state.getView().updateCellState(state); mxGeometry geo = graph.getCellGeometry(cell); // Moves selection cells and non-relative vertices in // the first phase so that edge terminal points will // be updated in the second phase if ((dx != 0 || dy != 0) && geo != null && (!geo.isRelative() || deltas.get(state) != null)) { state.setX(state.getX() + dx); state.setY(state.getY() + dy); } } int childCount = model.getChildCount(cell); for (int i = 0; i < childCount; i++) { translateState(state, graph.getView().getState(model.getChildAt(cell, i)), dx, dy); } } } /** * */ protected mxRectangle revalidateState(mxCellState parentState, mxCellState state, double dx, double dy) { mxRectangle dirty = null; if (state != null) { mxGraph graph = graphComponent.getGraph(); mxIGraphModel model = graph.getModel(); Object cell = state.getCell(); // Updates the edge terminal points and restores the // (relative) positions of any (relative) children if (model.isEdge(cell)) { state.getView().updateCellState(state); } dirty = state.getView().getBoundingBox(state, false); // Moves selection vertices which are relative mxGeometry geo = graph.getCellGeometry(cell); if ((dx != 0 || dy != 0) && geo != null && geo.isRelative() && model.isVertex(cell) && (parentState == null || model.isVertex(parentState.getCell()) || deltas .get(state) != null)) { state.setX(state.getX() + dx); state.setY(state.getY() + dy); // TODO: Check this change dirty.setX(dirty.getX() + dx); dirty.setY(dirty.getY() + dy); graph.getView().updateLabelBounds(state); } int childCount = model.getChildCount(cell); for (int i = 0; i < childCount; i++) { mxRectangle tmp = revalidateState(state, graph.getView() .getState(model.getChildAt(cell, i)), dx, dy); if (dirty != null) { dirty.add(tmp); } else { dirty = tmp; } } } return dirty; } /** * */ public void addEdges(mxCellState state) { mxGraph graph = graphComponent.getGraph(); mxIGraphModel model = graph.getModel(); Object cell = state.getCell(); int edgeCount = model.getEdgeCount(cell); for (int i = 0; i < edgeCount; i++) { mxCellState state2 = graph.getView().getState( model.getEdgeAt(cell, i)); if (state2 != null) { moveState(state2, 0, 0); } } } /** * */ public void paint(Graphics g) { if (cellStates != null && cellStates.size() > 0) { mxGraphics2DCanvas canvas = graphComponent.getCanvas(); // Sets antialiasing if (graphComponent.isAntiAlias()) { mxUtils.setAntiAlias((Graphics2D) g, true, true); } Graphics2D previousGraphics = canvas.getGraphics(); Point previousTranslate = canvas.getTranslate(); double previousScale = canvas.getScale(); try { canvas.setScale(graphComponent.getGraph().getView().getScale()); canvas.setTranslate(0, 0); canvas.setGraphics((Graphics2D) g); paintPreview(canvas); } finally { canvas.setScale(previousScale); canvas.setTranslate(previousTranslate.x, previousTranslate.y); canvas.setGraphics(previousGraphics); } } } /** * */ protected float getOpacityForCell(Object cell) { return opacity; } /** * Draws the preview using the graphics canvas. */ protected void paintPreview(mxGraphics2DCanvas canvas) { Composite previousComposite = canvas.getGraphics().getComposite(); // Paints the preview states Iterator<mxCellState> it = cellStates.iterator(); while (it.hasNext()) { mxCellState state = it.next(); canvas.getGraphics().setComposite( AlphaComposite.getInstance(AlphaComposite.SRC_OVER, getOpacityForCell(state.getCell()))); paintPreviewState(canvas, state); } canvas.getGraphics().setComposite(previousComposite); } /** * Draws the preview using the graphics canvas. */ protected void paintPreviewState(mxGraphics2DCanvas canvas, mxCellState state) { graphComponent.getGraph().drawState( canvas, state, state.getCell() != graphComponent.getCellEditor() .getEditingCell()); } }