| /******************************************************************************* |
| * 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); |
| } |
| |
| // adjust the arcs going from source to destination |
| adjustCurves(rightList); |
| // adjust the arcs going from destination to source |
| if (leftList != null) { |
| adjustCurves(leftList); |
| } |
| } |
| |
| /** |
| * Takes a list of IGraphModelConnections and adjusts the curve depths and |
| * the bezier curves based on the number of curves in the list. |
| * |
| * @param rightList |
| */ |
| protected void adjustCurves(List connections) { |
| // @tag TODO curves : add back this code to adjust the curves |
| // int scale = 3; |
| // for (int i = 0; i < connections.size(); i++) { |
| // GraphConnection conn = (GraphConnection) connections.get(i); |
| // if (conn.getSource() == conn.getDestination()) { |
| // scale = 5; |
| // } |
| // // even if the connection isn't curved in the style, the edit part |
| // // may decide that it should be curved if source and dest are equal. |
| // // @tag drawing(arcs) : check here if arcs are too close when being |
| // // drawn. Adjust the constant. |
| // int lineWidth = conn.getLineWidth(); |
| // conn.setCurveDepth((i + 1) * (scale + lineWidth)); |
| // |
| // // @tag zest(bug(152530-Bezier(fix))) : set the angles, etc based on |
| // // the count. |
| // // limit the angle to 90 degrees. |
| // conn.setStartAngle(90.0 - 85.0 / Math.pow(i, 1.0 / 9.0)); |
| // conn.setEndAngle(85.0 / Math.pow(i, 1.0 / 9.0) - 90.0); |
| // // limit the length to 1 |
| // conn.setStartLength(.75 - .25 / (Math.sqrt(i))); |
| // conn.setEndLength(.75 - .25 / (Math.sqrt(i))); |
| // } |
| } |
| |
| /** |
| * @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(source); |
| } |
| 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; |
| } |
| } |