org.jgraph.graph.AbstractCellView Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ingeniasjgraphmod Show documentation
Show all versions of ingeniasjgraphmod Show documentation
A modified version of some JGraph files
The newest version!
/*
* @(#)AbstractCellView.java 1.0 03-JUL-04
*
* Copyright (c) 2001-2005 Gaudenz Alder
*
* See LICENSE file in distribution for licensing details of this source file
*/
package org.jgraph.graph;
import java.awt.Component;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Stack;
import javax.swing.tree.DefaultMutableTreeNode;
import org.jgraph.JGraph;
/**
* The abstract base class for all cell views.
*
* @version 1.0 1/3/02
* @author Gaudenz Alder
*/
public abstract class AbstractCellView implements CellView, Serializable {
/** Editor for the cell. */
public static transient GraphCellEditor cellEditor;
// Headless environment may have no default fonts installed
static {
try {
cellEditor = new DefaultGraphCellEditor();
} catch (Error e) {
// No cell editor
}
}
/** Reference to the cell for this view */
protected Object cell = null;
/** Cached parent view */
protected CellView parent = null;
/** Cached child views. Default is a ArrayList with allocation size 0. */
protected java.util.List childViews = new ArrayList(0);
/**
* Contains the complete set of attributes, including the cell's attributes.
* The values in this map are overriden by the corresponding values in
* attributes
.
*/
protected AttributeMap allAttributes = createAttributeMap();
/**
* Hashtable for attributes. Value in this map override the values in
* allAttributes
.
*/
protected AttributeMap attributes = allAttributes;
/** Cached bounds of all children if vertex is a group */
protected transient Rectangle2D groupBounds = VertexView.defaultBounds;
/**
* Constructs an empty abstract cell view. You should set a cell on this
* view using setCell before doing anything. Optionally you can also set a
* different attribute map using setAttributeMap. Note: To change the
* attribute map you should now use the changeAttributes method.
*/
public AbstractCellView() {
}
/**
* Constructs a view for the specified model object, and invokes update on
* the new instance.
*
* @param cell
* reference to the model object
*/
public AbstractCellView(Object cell) {
setCell(cell);
}
/**
* Hook for subclassers to avoid creating an empty AttributeMap during
* construction of the instance. Override this and return null if you want
* to avoid creation of an attribute map at construction time.
*/
protected AttributeMap createAttributeMap() {
return new AttributeMap();
}
/**
* Returns the model object that this view represents.
*
* @return the model object that this view represents
*/
public Object getCell() {
return cell;
}
/**
* Sets the model object that this view represents to the specified cell
*
* @param cell
* the model object this view will represent
*/
public void setCell(Object cell) {
this.cell = cell;
}
/**
* Create child views and reload properties for this view. Invokes update
* first.
*
* @param cache
* the graph model to be used
* @param mapper
* the cell mapper to be used
* @param createDependentViews
* whether or not to create a view if one does not already exist
*/
public void refresh(GraphLayoutCache cache, CellMapper mapper,
boolean createDependentViews) {
// Re-read global attributes
GraphModel model = cache.getModel();
allAttributes = getCellAttributes(model);
// Cache Parent View
if (mapper != null && model != null) {
// Create parent only if it's visible in the graph
Object par = model.getParent(cell);
CellView tmp = mapper.getMapping(par, createDependentViews);
if (tmp != parent)
removeFromParent();
parent = tmp;
}
// Cache Cell Attributes in View
update(cache);
// Re-load Child Views
childViews.clear();
for (int i = 0; i < model.getChildCount(cell); i++) {
Object child = model.getChild(cell, i);
CellView view = mapper.getMapping(child, createDependentViews);
if (!model.isPort(child) && view != null)
childViews.add(view);
}
}
/**
* Hook for subclassers to avoid cloning the cell's attributes. Return
* model.getAttributes(cell) to avoid cloning.
*/
protected AttributeMap getCellAttributes(GraphModel model) {
return (AttributeMap) model.getAttributes(cell).clone();
}
/**
* Update attributes for this view and indicate to the parent this child has
* been updated
*/
public void update(GraphLayoutCache cache) {
mergeAttributes();
// Notify Parent
groupBounds = null;
childUpdated();
}
/**
* Implements the merging of the cell's attributes, initially stored in
* allAttributes, and the location attributes. The result should be stored
* in allAttributes. This hook is for subclassers to change the merging
* strategy.
*/
protected void mergeAttributes() {
allAttributes.putAll(attributes);
}
/**
* Indicates to parent, if any, that this child has been updated.
*/
public void childUpdated() {
if (parent != null)
parent.childUpdated();
groupBounds = null;
}
//
// Graph Structure
//
/**
* Returns the parent view for this view.
*
* @return the parent view for this view
*/
public CellView getParentView() {
return parent;
}
/**
* Returns the child views of this view.
*
* @return the child views of this view
*/
public CellView[] getChildViews() {
CellView[] array = new CellView[childViews.size()];
childViews.toArray(array);
return array;
}
/**
* Returns all views, including descendants that have a parent in
* views
without the PortViews. Note: Iterative
* Implementation using view.getChildViews. This returns the array in
* inverse order, ie with the top most cell view at index 0.
*
* @param views
* the cell views whose descendants are to be returned
* @return the specified views and all their descendant views
*/
public static CellView[] getDescendantViews(CellView[] views) {
Stack stack = new Stack();
for (int i = 0; i < views.length; i++)
stack.add(views[i]);
ArrayList result = new ArrayList();
while (!stack.isEmpty()) {
CellView tmp = (CellView) stack.pop();
Object[] children = tmp.getChildViews();
for (int i = 0; i < children.length; i++)
stack.add(children[i]);
result.add(tmp);
}
CellView[] ret = new CellView[result.size()];
result.toArray(ret);
return ret;
}
/**
* Removes this view from the list of children of the parent.
*/
public void removeFromParent() {
if (parent instanceof AbstractCellView) {
java.util.List list = ((AbstractCellView) parent).childViews;
// TODO performance could be quadratic
list.remove(this);
}
}
/**
* Returns true
if the view is a leaf.
*
* @return true
if the view is a leaf
*/
public boolean isLeaf() {
return childViews.isEmpty();
}
//
// View Attributes
//
/**
* Return the attributes of the view.
*
* @return the attributes
of this view
*/
public AttributeMap getAttributes() {
return attributes;
}
/**
* Sets the attributes of this view to the specified value
*
* @param attributes
* the new attributes to set
*/
public void setAttributes(AttributeMap attributes) {
this.attributes = attributes;
}
/**
* Returns the attributes of the view combined with the attributes of the
* corresponding cell. The view's attributes override the cell's attributes
* with the same key.
*/
public AttributeMap getAllAttributes() {
return allAttributes;
}
/**
* Applies change
to the attributes of the view and calls
* update.
*
* @param change
* a map of attribute changes to apply
* @return the undo map that reverses this change
*/
public Map changeAttributes(GraphLayoutCache cache, Map change) {
if (change != null) {
Map undo = attributes.applyMap(change);
update(cache);
return undo;
}
return null;
}
//
// View Methods
//
/**
* Returns the cached bounds for the group if isleaf is false
*/
public Rectangle2D getBounds() {
if (!isLeaf()) {
if (groupBounds == null)
updateGroupBounds();
return groupBounds;
}
return null;
}
/**
* Returns the bounding box for the specified views.
*
* @param views
* the views for whom the bounding box is to be determined
* @return the bounding box of the specified views
*/
public static Rectangle2D getBounds(CellView[] views) {
if (views != null && views.length > 0) {
Rectangle2D ret = null;
for (int i = 0; i < views.length; i++) {
if (views[i] != null) {
Rectangle2D r = views[i].getBounds();
if (r != null) {
if (ret == null)
ret = new Rectangle2D.Double(r.getX(), r.getY(), r
.getWidth(), r.getHeight());
else
Rectangle2D.union(ret, r, ret);
}
}
}
return ret;
}
return null;
}
/**
* Sets the bounds of this view
. Calls translateView and
* scaleView.
*
* @param bounds
* the new bounds for this cell view
*/
public void setBounds(Rectangle2D bounds) {
Rectangle2D oldBounds = getBounds();
if (oldBounds == null)
oldBounds = new Rectangle2D.Double();
Point2D p0 = new Point2D.Double(oldBounds.getX(), oldBounds.getY());
Point2D pe = new Point2D.Double(bounds.getX(), bounds.getY());
Rectangle2D localBounds = new Rectangle2D.Double(bounds.getX(), bounds
.getY(), bounds.getWidth(), bounds.getHeight());
if (GraphConstants.isMoveable(getAllAttributes()) && !pe.equals(p0))
translate(pe.getX() - p0.getX(), pe.getY() - p0.getY());
else
localBounds.setFrame(localBounds.getX(), localBounds.getY(), bounds
.getWidth()
- pe.getX() + p0.getX(), bounds.getHeight() - pe.getY()
+ p0.getY());
double lbw = localBounds.getWidth(), lbh = localBounds.getHeight();
double obw = oldBounds.getWidth(), obh = oldBounds.getHeight();
if ((lbw != obw || lbh != obh) && obw > 0 && obh > 0)
scale(lbw / obw, lbh / obh, pe);
}
/**
* Updates the bounds of this view and its children
*
*/
protected void updateGroupBounds() {
// Note: Prevent infinite recursion by removing
// child edges that point to their parent.
CellView[] childViews = getChildViews();
LinkedList result = new LinkedList();
for (int i = 0; i < childViews.length; i++)
if (includeInGroupBounds(childViews[i]))
result.add(childViews[i]);
childViews = new CellView[result.size()];
result.toArray(childViews);
Rectangle2D r = getBounds(childViews);
int groupBorder = GraphConstants.getInset(getAllAttributes());
if (r != null)
r.setFrame(r.getX() - groupBorder, r.getY() - groupBorder, r
.getWidth()
+ 2 * groupBorder, r.getHeight() + 2 * groupBorder);
groupBounds = r;
}
/**
* This is used to exclude certain cell views from the group bounds
* computation. This implementation returns false for edges that connect to
* one of their ancestor groups (eg. parent).
*
* @param view
* the cell view to be included in the group bounds or not
* @return whether or not to include the specified cell in the group bounds
*/
protected boolean includeInGroupBounds(CellView view) {
if (view instanceof EdgeView
&& getCell() instanceof DefaultMutableTreeNode) {
EdgeView edgeView = (EdgeView) view;
if (edgeView.getCell() instanceof DefaultMutableTreeNode) {
DefaultMutableTreeNode edge = (DefaultMutableTreeNode) edgeView
.getCell();
Object src = null;
if (edgeView.getSource() != null
&& edgeView.getSource().getParentView() != null)
src = edgeView.getSource().getParentView().getCell();
else if (edgeView.getSourceParentView() != null)
src = edgeView.getSourceParentView().getCell();
if (src instanceof DefaultMutableTreeNode) {
DefaultMutableTreeNode source = (DefaultMutableTreeNode) src;
if (source.isNodeDescendant(edge))
return false;
}
Object tgt = null;
if (edgeView.getTarget() != null
&& edgeView.getTarget().getParentView() != null)
tgt = edgeView.getTarget().getParentView().getCell();
else if (edgeView.getTargetParentView() != null)
tgt = edgeView.getTargetParentView().getCell();
if (tgt instanceof DefaultMutableTreeNode) {
DefaultMutableTreeNode target = (DefaultMutableTreeNode) tgt;
if (target.isNodeDescendant(edge)) {
return false;
}
}
}
}
return true;
}
/**
* Translates view
(group) by dx, dy
.
*
* @param dx
* the x-coordinate amount to translate by
* @param dy
* the y-coordinate amount to translate by
*/
public void translate(double dx, double dy) {
if (isLeaf())
getAllAttributes().translate(dx, dy);
else {
int moveableAxis = GraphConstants
.getMoveableAxis(getAllAttributes());
if (moveableAxis == GraphConstants.X_AXIS)
dy = 0;
else if (moveableAxis == GraphConstants.Y_AXIS)
dx = 0;
Iterator it = childViews.iterator();
while (it.hasNext()) {
Object view = it.next();
if (view instanceof AbstractCellView) {
AbstractCellView child = (AbstractCellView) view;
child.translate(dx, dy);
}
}
}
}
/**
* Scale view
(group) by sx, sy
.
*
* @param sx
* the multiple by which the x coordinate position of the cell
* view is to be scaled
* @param sy
* the multiple by which the y coordinate position of the cell
* view is to be scaled
* @param origin
* the origin point from which the scaling will calculate
*/
public void scale(double sx, double sy, Point2D origin) {
if (isLeaf())
getAttributes().scale(sx, sy, origin);
else {
int sizeableAxis = GraphConstants
.getSizeableAxis(getAllAttributes());
if (sizeableAxis == GraphConstants.X_AXIS)
sy = 1;
else if (sizeableAxis == GraphConstants.Y_AXIS)
sx = 1;
Iterator it = childViews.iterator();
while (it.hasNext()) {
Object view = it.next();
if (view instanceof AbstractCellView) {
AbstractCellView child = (AbstractCellView) view;
Map attrs = child.getAttributes();
if (GraphConstants.isSizeable(attrs)
|| GraphConstants.isAutoSize(attrs))
child.scale(sx, sy, origin);
}
}
}
}
/**
* Returns true if the view intersects the given rectangle.
*
* @param graph
* the JGraph
instance of the view
* @param rect
* the rectangle within which intersection is being checked for
*
* @return whether or not the rectangle specified intersects the view
*/
public boolean intersects(JGraph graph, Rectangle2D rect) {
if (isLeaf() || GraphConstants.isGroupOpaque(getAllAttributes())) {
Rectangle2D bounds = getBounds();
if (bounds != null)
return bounds.intersects(rect);
} else { // Check If Children Intersect
Iterator it = childViews.iterator();
while (it.hasNext())
if (((CellView) it.next()).intersects(graph, rect))
return true;
}
return false;
}
//
// View Editors
//
/**
* Returns a renderer component, configured for the view. The method used to
* obtain the renderer instance must install the necessary attributes from
* this view
*
* @param graph
* the JGraph
instance of the view
* @param selected
* whether or not this view is selected
* @param focus
* whether or not this view is the focus
* @param preview
* whether or not it is a preview of the view
*
* @return the renderer component for this view with this views attributes
* installed
*/
public Component getRendererComponent(JGraph graph, boolean selected,
boolean focus, boolean preview) {
CellViewRenderer cvr = getRenderer();
if (cvr != null)
return cvr.getRendererComponent(graph, this, selected, focus,
preview);
return null;
}
/**
* Obtains the renderer instance for this view
*
* @return the renderer instance for this view
*/
public abstract CellViewRenderer getRenderer();
/**
* Returns a cell handle for the view.
*
* @param context
* the context of this cell view (cells indirectly affected by
* it)
* @return the cell handle for this view
*/
public abstract CellHandle getHandle(GraphContext context);
/**
* Returns a cell editor for the view.
*
* @return the cell editor for this view
*/
public GraphCellEditor getEditor() {
return cellEditor;
}
public static Point2D getCenterPoint(CellView vertex) {
Rectangle2D r = vertex.getBounds();
if (r != null)
return new Point2D.Double(r.getCenterX(), r.getCenterY());
return null;
}
/**
* Returns the intersection of the bounding rectangle and the straight line
* between the source and the specified point p. The specified point is
* expected not to intersect the bounds. Note: You must override this method
* if you use a different renderer. This is because this method relies on
* the VertexRenderer interface, which can not be safely assumed for
* subclassers.
*/
public Point2D getPerimeterPoint(EdgeView edge, Point2D source, Point2D p) {
return getCenterPoint(this);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy