blob: 2adffcdd1f63a0a606a650d906f214ffa73c1a10 [file] [log] [blame]
/*******************************************************************************
* Copyright 2005, 2009, CHISEL Group, University of Victoria, Victoria, BC,
* Canada. All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v1.0 which
* accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors: The Chisel Group, University of Victoria
******************************************************************************/
package org.eclipse.zest.core.viewers.internal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.eclipse.draw2d.IFigure;
import org.eclipse.jface.viewers.IBaseLabelProvider;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.swt.SWT;
import org.eclipse.zest.core.viewers.IFigureProvider;
import org.eclipse.zest.core.viewers.INestedContentProvider;
import org.eclipse.zest.core.widgets.Graph;
import org.eclipse.zest.core.widgets.GraphConnection;
import org.eclipse.zest.core.widgets.GraphItem;
import org.eclipse.zest.core.widgets.GraphNode;
import org.eclipse.zest.core.widgets.IContainer;
/**
* Base class that can be used for model factories. Offers facilities to style
* the items that have been created by the factory.
*
* @author Del Myers
*/
// @tag zest.bug.160367-Refreshing.fix : update the factory to use the
// IStylingGraphModelFactory
public abstract class AbstractStylingModelFactory implements
IStylingGraphModelFactory {
private AbstractStructuredGraphViewer viewer;
private int connectionStyle;
private int nodeStyle;
private List /* ConstraintAdapater */constraintAdapters = new ArrayList();
/**
*
*/
public AbstractStylingModelFactory(AbstractStructuredGraphViewer viewer) {
this.viewer = viewer;
this.connectionStyle = SWT.NONE;
this.nodeStyle = SWT.NONE;
if (viewer instanceof AbstractStructuredGraphViewer) {
this.constraintAdapters = (viewer).getConstraintAdapters();
}
}
public void styleConnection(GraphConnection conn) {
// recount the source and target connections on the node.
// this isn't a great way to do it, because it results in
// an n^2 algorithm. But, if anyone can figure out a better way
// go ahead and try it.
GraphNode source = conn.getSource();
GraphNode dest = conn.getDestination();
LinkedList rightList = getConnectionList(source, dest);
LinkedList leftList = null;
if (dest != source) {
leftList = getConnectionList(dest, source);
}
int size = (leftList != null) ? leftList.size() + rightList.size()
: rightList.size();
// adjust the arcs going from source to destination
adjustCurves(rightList, size);
// adjust the arcs going from destination to source
if (leftList != null) {
adjustCurves(leftList, size);
}
}
/**
* Takes a list of IGraphModelConnections and adjusts the curve depths and
* the bezier curves based on the number of curves in the list.
*
* @param connections
* @param size
* + * total number of arcs - may be bigger then connections.size
*/
protected void adjustCurves(List connections, int size) {
/*
* The connections should be curved if source and dest are equal, or
* there are multiple arcs between two nodes
*/
for (int i = 0; i < connections.size(); i++) {
GraphConnection conn = (GraphConnection) connections.get(i);
int radius = 20;
if (conn.getSource() == conn.getDestination()) {
radius = 40;
} else if (size < 2) {
radius = 0;
}
conn.setCurveDepth((i + 1) * radius);
}
}
/**
* @param source
* @param dest
* @return
*/
private LinkedList getConnectionList(GraphNode source, GraphNode dest) {
LinkedList list = new LinkedList();
Iterator i = source.getSourceConnections().iterator();
while (i.hasNext()) {
GraphConnection c = (GraphConnection) i.next();
if (c.getDestination() == dest) {
list.add(c);
}
}
return list;
}
public void styleItem(GraphItem item) {
GraphItemStyler.styleItem(item, getLabelProvider());
if (item instanceof GraphConnection) {
styleConnection((GraphConnection) item);
}
}
public StructuredViewer getViewer() {
return viewer;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#
* getLabelProvider()
*/
public IBaseLabelProvider getLabelProvider() {
return viewer.getLabelProvider();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#
* getContentProvider()
*/
public IStructuredContentProvider getContentProvider() {
return (IStructuredContentProvider) viewer.getContentProvider();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#
* createConnection(org.eclipse.zest.core.internal.graphmodel.GraphModel,
* java.lang.Object, java.lang.Object, java.lang.Object)
*/
public GraphConnection createConnection(Graph graph, Object element,
Object source, Object dest) {
if (source == null || dest == null) {
return null;
}
GraphConnection oldConnection = viewer.getGraphModelConnection(element);
GraphNode sn = viewer.getGraphModelNode(source);
GraphNode dn = viewer.getGraphModelNode(dest);
if (oldConnection != null) {
if (sn != oldConnection.getSource()
|| dn != oldConnection.getDestination()) {
viewer.removeGraphModelConnection(oldConnection);
} else {
styleItem(oldConnection);
return oldConnection;
}
}
IFigureProvider figureProvider = null;
if (getLabelProvider() instanceof IFigureProvider) {
figureProvider = (IFigureProvider) getLabelProvider();
}
if (sn == null) {
IFigure figure = null;
if (figureProvider != null) {
figure = figureProvider.getFigure(source);
}
if (figure != null) {
sn = createNode(graph, source, figure);
} else {
sn = createNode(graph, source);
}
}
if (dn == null) {
IFigure figure = null;
if (figureProvider != null) {
figure = figureProvider.getFigure(dest);
}
if (figure != null) {
dn = createNode(graph, dest, figure);
} else {
dn = createNode(graph, dest);
}
}
GraphConnection c = viewer.addGraphModelConnection(element, sn, dn);
styleItem(c);
return c;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#
* createNode(org.eclipse.zest.core.internal.graphmodel.GraphModel,
* java.lang.Object)
*/
public GraphNode createNode(Graph graph, Object element, IFigure figure) {
GraphNode node = null;
if (getContentProvider() instanceof INestedContentProvider) {
boolean isContainer = ((INestedContentProvider) getContentProvider())
.hasChildren(element);
if (isContainer) {
node = viewer.addGraphModelContainer(element);
styleItem(node);
Object[] childNodes = ((INestedContentProvider) getContentProvider())
.getChildren(element);
childNodes = filter(getViewer().getInput(), childNodes);
if (childNodes == null) {
return node;
}
for (int i = 0; i < childNodes.length; i++) {
GraphNode childNode = viewer.addGraphModelNode(
(IContainer) node, childNodes[i]);
styleItem(childNode);
}
((IContainer) node).applyLayout();
return node;
}
}
node = viewer.addGraphModelNode(element, figure);
styleItem(node);
return node;
}
public GraphNode createNode(Graph graph, Object element) {
IFigure nodeFigure = null;
if (getLabelProvider() instanceof IFigureProvider) {
nodeFigure = ((IFigureProvider) getLabelProvider())
.getFigure(element);
}
return this.createNode(graph, element, nodeFigure);
}
public void setConnectionStyle(int style) {
this.connectionStyle = style;
}
/**
* @return the connectionStyle
*/
public int getConnectionStyle() {
return connectionStyle;
}
public void setNodeStyle(int style) {
this.nodeStyle = style;
}
public List /* ConstraintAdapter */getConstraintAdapters() {
return this.constraintAdapters;
}
/**
* @return the nodeStyle
*/
public int getNodeStyle() {
return nodeStyle;
}
/**
* Default implementation simply restyles the item, regardless of the
* properties.
*/
public void update(GraphItem item) {
styleItem(item);
}
/**
* Default implementation simply restyles the items, regardless of the
* properties.
*/
public void update(GraphItem[] items) {
for (int i = 0; i < items.length; i++) {
styleItem(items[i]);
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#
* refreshGraph(org.eclipse.zest.core.internal.graphmodel.GraphModel)
*/
public void refreshGraph(Graph graph) {
// with this kind of graph, it is just as easy and cost-effective to
// rebuild the whole thing.
Map oldMap = viewer.getNodesMap();
HashMap nodesMap = new HashMap();
// have to copy the Map data accross so that it doesn't get overwritten
for (Iterator keys = oldMap.keySet().iterator(); keys.hasNext();) {
Object key = keys.next();
nodesMap.put(key, oldMap.get(key));
}
clearGraph(graph);
doBuildGraph(graph);
// update the positions on the new nodes to match the old ones.
GraphNode[] nodes = getNodesArray(graph);
// save a little time, go with the smallest list as the primary list
if (nodes.length < nodesMap.keySet().size()) {
for (int i = 0; i < nodes.length; i++) {
GraphNode oldNode = (GraphNode) nodesMap
.get(nodes[i].getData());
if (oldNode != null) {
nodes[i].setLocation(oldNode.getLocation().x,
oldNode.getLocation().y);
if (oldNode.isSizeFixed()) {
nodes[i].setSize(oldNode.getSize().width,
oldNode.getSize().height);
}
}
}
} else {
for (Iterator i = nodesMap.keySet().iterator(); i.hasNext();) {
Object key = i.next();
GraphNode node = viewer.getGraphModelNode(key);
if (node != null) {
GraphNode oldNode = (GraphNode) nodesMap.get(key);
node.setLocation(oldNode.getLocation().x,
oldNode.getLocation().y);
if (oldNode.isSizeFixed()) {
node.setSize(oldNode.getSize().width,
oldNode.getSize().height);
}
}
}
}
}
/**
* Convenience method for clearing all the elements in the graph.
*
* @param graph
*/
public void clearGraph(Graph graph) {
graph.setSelection(null);
Object[] nodeElements = viewer.getNodeElements();
for (int i = 0; i < nodeElements.length; i++) {
viewer.removeGraphModelNode(nodeElements[i]);
}
Object[] connectionElements = viewer.getConnectionElements();
for (int i = 0; i < connectionElements.length; i++) {
viewer.removeGraphModelConnection(connectionElements[i]);
}
}
/**
* Builds the graph model from the viewer's content provider. There is no
* guarantee that the model will be cleared before this method is called.
*
* @param graph
*/
protected void doBuildGraph(Graph model) {
clearGraph(model);
model.setConnectionStyle(getConnectionStyle());
model.setNodeStyle(getNodeStyle());
model.setConstraintAdapters(getConstraintAdapters());
}
/**
* Determines if this element should be filtered or not.
*
* @param parent
* @param element
* @return
*/
protected boolean filterElement(Object parent, Object element) {
ViewerFilter[] filters = getViewer().getFilters();
for (int i = 0; i < filters.length; i++) {
boolean selected = filters[i].select(viewer, parent, element);
if (!selected) {
return true;
}
}
return false;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#
* isFiltered(java.lang.Object)
*/
protected Object[] filter(Object parent, Object[] elements) {
Object[] result = elements;
ViewerFilter[] filters = getViewer().getFilters();
for (int i = 0; i < filters.length; i++) {
result = filters[i].filter(viewer, parent, result);
}
return result;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#refresh
* (org.eclipse.zest.core.internal.graphmodel.GraphModel, java.lang.Object)
*/
public void refresh(Graph graph, Object element) {
refresh(graph, element, false);
}
/**
* Converts the list of GraphNode objects into an array and return it.
*
* @return GraphModelNode[]
*/
protected GraphNode[] getNodesArray(Graph graph) {
GraphNode[] nodesArray = new GraphNode[graph.getNodes().size()];
nodesArray = (GraphNode[]) graph.getNodes().toArray(nodesArray);
return nodesArray;
}
/**
* Converts the list of GraphConnections objects into an array and return
* it.
*
* @param graph
* @return
*/
protected GraphConnection[] getConnectionArray(Graph graph) {
GraphConnection[] connectionArray = new GraphConnection[graph
.getConnections().size()];
connectionArray = (GraphConnection[]) graph.getConnections().toArray(
connectionArray);
return connectionArray;
}
}