Download
Getting Started
Members
Projects
Community
Marketplace
Events
Planet Eclipse
Newsletter
Videos
Participate
Report a Bug
Forums
Mailing Lists
Wiki
IRC
How to Contribute
Working Groups
Automotive
Internet of Things
LocationTech
Long-Term Support
PolarSys
Science
OpenMDM
More
Community
Marketplace
Events
Planet Eclipse
Newsletter
Videos
Participate
Report a Bug
Forums
Mailing Lists
Wiki
IRC
How to Contribute
Working Groups
Automotive
Internet of Things
LocationTech
Long-Term Support
PolarSys
Science
OpenMDM
Toggle navigation
Bugzilla – Attachment 162001 Details for
Bug 277534
[tree] Add Space Tree support to Graph widget
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
Log In
[x]
|
Terms of Use
|
Copyright Agent
Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read
this important communication.
[patch]
Updated patch
Zest2_0.patch (text/plain), 1.15 MB, created by
Ian Bull
on 2010-03-14 23:50:16 EDT
(
hide
)
Description:
Updated patch
Filename:
MIME Type:
Creator:
Ian Bull
Created:
2010-03-14 23:50:16 EDT
Size:
1.15 MB
patch
obsolete
>### Eclipse Workspace Patch 1.0 >#P org.eclipse.zest.core >Index: META-INF/MANIFEST.MF >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/META-INF/MANIFEST.MF,v >retrieving revision 1.30 >diff -u -r1.30 MANIFEST.MF >--- META-INF/MANIFEST.MF 24 Jul 2009 19:14:05 -0000 1.30 >+++ META-INF/MANIFEST.MF 15 Mar 2010 03:48:52 -0000 >@@ -4,12 +4,13 @@ > Bundle-SymbolicName: org.eclipse.zest.core;singleton:=true > Bundle-Vendor: %Plugin.providerName > Bundle-Localization: plugin >-Bundle-Version: 1.2.0.qualifier >-Require-Bundle: org.eclipse.zest.layouts, >- org.eclipse.ui, >- org.eclipse.draw2d;visibility:=reexport >+Bundle-Version: 2.0.0.qualifier >+Require-Bundle: org.eclipse.ui, >+ org.eclipse.draw2d;visibility:=reexport, >+ org.eclipse.zest.layout_2_0;bundle-version="1.0.0" > Eclipse-LazyStart: false > Export-Package: org.eclipse.zest.core.viewers, >- org.eclipse.zest.core.widgets >+ org.eclipse.zest.core.widgets, >+ org.eclipse.zest.core.widgets.custom > Import-Package: com.ibm.icu.text;version="[3.8.1,5.0.0)" > Bundle-RequiredExecutionEnvironment: J2SE-1.4 >Index: src/org/eclipse/zest/core/viewers/AbstractStructuredGraphViewer.java >=================================================================== >RCS file: src/org/eclipse/zest/core/viewers/AbstractStructuredGraphViewer.java >diff -N src/org/eclipse/zest/core/viewers/AbstractStructuredGraphViewer.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/zest/core/viewers/AbstractStructuredGraphViewer.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,801 @@ >+/******************************************************************************* >+ * Copyright 2005-2010, 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 Mateusz Matela >+ * <mateusz.matela@gmail.com> - Adapt Zest to changes in layout - >+ * https://bugs.eclipse.org/bugs/show_bug.cgi?id=283179 >+ ******************************************************************************/ >+package org.eclipse.zest.core.viewers; >+ >+import java.util.ArrayList; >+import java.util.Comparator; >+import java.util.HashMap; >+import java.util.Iterator; >+import java.util.LinkedList; >+import java.util.List; >+import java.util.Map; >+import java.util.TreeSet; >+ >+import org.eclipse.draw2d.IFigure; >+import org.eclipse.swt.SWT; >+import org.eclipse.swt.SWTError; >+import org.eclipse.swt.events.DisposeEvent; >+import org.eclipse.swt.widgets.Widget; >+import org.eclipse.zest.core.viewers.internal.IStylingGraphModelFactory; >+import org.eclipse.zest.core.widgets.Graph; >+import org.eclipse.zest.core.widgets.GraphConnection; >+import org.eclipse.zest.core.widgets.GraphContainer; >+import org.eclipse.zest.core.widgets.GraphItem; >+import org.eclipse.zest.core.widgets.GraphNode; >+import org.eclipse.zest.core.widgets.ZestStyles; >+import org.eclipse.zest.core.widgets.custom.CGraphNode; >+import org.eclipse.zest.layouts.LayoutAlgorithm; >+ >+/** >+ * Abstraction of graph viewers to implement functionality used by all of them. >+ * Not intended to be implemented by clients. Use one of the provided children >+ * instead. >+ * >+ * @author Del Myers >+ * @since 2.0 >+ */ >+public abstract class AbstractStructuredGraphViewer extends >+ AbstractZoomableViewer { >+ /** >+ * Contains top-level styles for the entire graph. Set in the constructor. * >+ */ >+ private int graphStyle; >+ >+ /** >+ * Contains node-level styles for the graph. Set in setNodeStyle(). Defaults >+ * are used in the constructor. >+ */ >+ private int nodeStyle; >+ >+ /** >+ * Contains arc-level styles for the graph. Set in setConnectionStyle(). >+ * Defaults are used in the constructor. >+ */ >+ private int connectionStyle; >+ >+ private HashMap nodesMap = new HashMap(); >+ private HashMap connectionsMap = new HashMap(); >+ >+ /** >+ * A simple graph comparator that orders graph elements based on thier type >+ * (connection or node), and their unique object identification. >+ */ >+ private class SimpleGraphComparator implements Comparator { >+ TreeSet storedStrings; >+ >+ /** >+ * >+ */ >+ public SimpleGraphComparator() { >+ this.storedStrings = new TreeSet(); >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) >+ */ >+ public int compare(Object arg0, Object arg1) { >+ if (arg0 instanceof GraphNode && arg1 instanceof GraphConnection) { >+ return 1; >+ } else if (arg0 instanceof GraphConnection >+ && arg1 instanceof GraphNode) { >+ return -1; >+ } >+ if (arg0.equals(arg1)) { >+ return 0; >+ } >+ return getObjectString(arg0).compareTo(getObjectString(arg1)); >+ } >+ >+ private String getObjectString(Object o) { >+ String s = o.getClass().getName() + "@" >+ + Integer.toHexString(o.hashCode()); >+ while (storedStrings.contains(s)) { >+ s = s + 'X'; >+ } >+ return s; >+ } >+ } >+ >+ protected AbstractStructuredGraphViewer(int graphStyle) { >+ this.graphStyle = graphStyle; >+ this.connectionStyle = SWT.NONE; >+ this.nodeStyle = SWT.NONE; >+ >+ } >+ >+ /** >+ * Sets the default style for nodes in this graph. Note: if an input is set >+ * on the viewer, a ZestException will be thrown. >+ * >+ * @param nodeStyle >+ * the style for the nodes. >+ * @see #ZestStyles >+ */ >+ public void setNodeStyle(int nodeStyle) { >+ if (getInput() != null) { >+ throw new SWTError(SWT.ERROR_UNSPECIFIED); >+ } >+ this.nodeStyle = nodeStyle; >+ } >+ >+ /** >+ * Sets the default style for connections in this graph. Note: if an input >+ * is set on the viewer, a ZestException will be thrown. >+ * >+ * @param connectionStyle >+ * the style for the connections. >+ * @see #ZestStyles >+ */ >+ public void setConnectionStyle(int connectionStyle) { >+ if (getInput() != null) { >+ throw new SWTError(SWT.ERROR_UNSPECIFIED); >+ } >+ if (!ZestStyles.validateConnectionStyle(connectionStyle)) { >+ throw new SWTError(SWT.ERROR_INVALID_ARGUMENT); >+ } >+ this.connectionStyle = connectionStyle; >+ } >+ >+ /** >+ * Returns the style set for the graph >+ * >+ * @return The style set of the graph >+ */ >+ public int getGraphStyle() { >+ return graphStyle; >+ } >+ >+ /** >+ * Returns the style set for the nodes. >+ * >+ * @return the style set for the nodes. >+ */ >+ public int getNodeStyle() { >+ return nodeStyle; >+ } >+ >+ public Graph getGraphControl() { >+ return (Graph) getControl(); >+ } >+ >+ /** >+ * @return the connection style. >+ */ >+ public int getConnectionStyle() { >+ return connectionStyle; >+ } >+ >+ /** >+ * Sets the layout algorithm for this viewer. Subclasses may place >+ * restrictions on the algorithms that it accepts. >+ * >+ * @param algorithm >+ * the layout algorithm >+ * @param run >+ * true if the layout algorithm should be run immediately. This >+ * is a hint. >+ */ >+ public abstract void setLayoutAlgorithm(LayoutAlgorithm algorithm, >+ boolean run); >+ >+ /** >+ * Gets the current layout algorithm. >+ * >+ * @return the current layout algorithm. >+ */ >+ protected abstract LayoutAlgorithm getLayoutAlgorithm(); >+ >+ /** >+ * Equivalent to setLayoutAlgorithm(algorithm, false). >+ * >+ * @param algorithm >+ */ >+ public void setLayoutAlgorithm(LayoutAlgorithm algorithm) { >+ setLayoutAlgorithm(algorithm, false); >+ } >+ >+ public Object[] getNodeElements() { >+ return this.nodesMap.keySet().toArray(); >+ } >+ >+ public Object[] getConnectionElements() { >+ return this.connectionsMap.keySet().toArray(); >+ } >+ >+ /** >+ * @noreference This method is not intended to be referenced by clients. >+ * @return >+ */ >+ public HashMap getNodesMap() { >+ return this.nodesMap; >+ } >+ >+ /** >+ * >+ * @param element >+ * @return >+ * @noreference This method is not intended to be referenced by clients. >+ */ >+ public GraphNode addGraphModelContainer(Object element) { >+ GraphNode node = this.getGraphModelNode(element); >+ if (node == null) { >+ node = new GraphContainer((Graph) getControl(), SWT.NONE); >+ this.nodesMap.put(element, node); >+ node.setData(element); >+ } >+ return node; >+ } >+ >+ /** >+ * >+ * @param container >+ * @param element >+ * @return >+ * @noreference This method is not intended to be referenced by clients. >+ */ >+ public GraphNode addGraphModelNode(GraphContainer container, Object element) { >+ GraphNode node = this.getGraphModelNode(element); >+ if (node == null) { >+ node = new GraphNode(container, SWT.NONE); >+ this.nodesMap.put(element, node); >+ node.setData(element); >+ } >+ return node; >+ } >+ >+ /** >+ * >+ * @param element >+ * @param figure >+ * @return >+ * @noreference This method is not intended to be referenced by clients. >+ */ >+ public GraphNode addGraphModelNode(Object element, IFigure figure) { >+ GraphNode node = this.getGraphModelNode(element); >+ if (node == null) { >+ if (figure != null) { >+ node = new CGraphNode((Graph) getControl(), SWT.NONE, figure); >+ this.nodesMap.put(element, node); >+ node.setData(element); >+ } else { >+ node = new GraphNode((Graph) getControl(), SWT.NONE); >+ this.nodesMap.put(element, node); >+ node.setData(element); >+ } >+ } >+ return node; >+ } >+ >+ /** >+ * >+ * @param element >+ * @param source >+ * @param target >+ * @return >+ * @noreference This method is not intended to be referenced by clients. >+ */ >+ public GraphConnection addGraphModelConnection(Object element, >+ GraphNode source, GraphNode target) { >+ GraphConnection connection = this.getGraphModelConnection(element); >+ if (connection == null) { >+ connection = new GraphConnection((Graph) getControl(), SWT.NONE, >+ source, target); >+ this.connectionsMap.put(element, connection); >+ connection.setData(element); >+ } >+ return connection; >+ >+ } >+ >+ /** >+ * >+ * @param obj >+ * @return >+ * @noreference This method is not intended to be referenced by clients. >+ */ >+ public GraphConnection getGraphModelConnection(Object obj) { >+ return (GraphConnection) this.connectionsMap.get(obj); >+ } >+ >+ /** >+ * >+ * @param obj >+ * @return >+ * @noreference This method is not intended to be referenced by clients. >+ */ >+ public GraphNode getGraphModelNode(Object obj) { >+ return (GraphNode) this.nodesMap.get(obj); >+ } >+ >+ /** >+ * @param obj >+ * @noreference This method is not intended to be referenced by clients. >+ */ >+ public void removeGraphModelConnection(Object obj) { >+ GraphConnection connection = (GraphConnection) connectionsMap.get(obj); >+ if (connection != null) { >+ connectionsMap.remove(obj); >+ if (!connection.isDisposed()) { >+ connection.dispose(); >+ } >+ } >+ } >+ >+ /** >+ * @noreference This method is not intended to be referenced by clients. >+ */ >+ public void removeGraphModelNode(Object obj) { >+ GraphNode node = (GraphNode) nodesMap.get(obj); >+ if (node != null) { >+ nodesMap.remove(obj); >+ if (!node.isDisposed()) { >+ node.dispose(); >+ } >+ } >+ } >+ >+ protected void handleDispose(DisposeEvent event) { >+ >+ if (getControl() != null && !getControl().isDisposed()) { >+ getControl().dispose(); >+ } >+ super.handleDispose(event); >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see >+ * org.eclipse.jface.viewers.StructuredViewer#internalRefresh(java.lang. >+ * Object) >+ */ >+ protected void internalRefresh(Object element) { >+ if (getInput() == null) { >+ return; >+ } >+ if (element == getInput()) { >+ getFactory().refreshGraph(getGraphControl()); >+ } else { >+ getFactory().refresh(getGraphControl(), element); >+ } >+ // After all the items are loaded, we call update to ensure drawing. >+ // This way the damaged area does not get too big if we start >+ // adding and removing more nodes >+ getGraphControl().getLightweightSystem().getUpdateManager() >+ .performUpdate(); >+ } >+ >+ protected void doUpdateItem(Widget item, Object element, boolean fullMap) { >+ if (item == getGraphControl()) { >+ getFactory().update(getNodesArray(getGraphControl())); >+ getFactory().update(getConnectionsArray(getGraphControl())); >+ } else if (item instanceof GraphItem) { >+ getFactory().update((GraphItem) item); >+ } >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see >+ * org.eclipse.jface.viewers.StructuredViewer#doFindInputItem(java.lang. >+ * Object) >+ */ >+ protected Widget doFindInputItem(Object element) { >+ >+ if (element == getInput() && element instanceof Widget) { >+ return (Widget) element; >+ } >+ return null; >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see >+ * org.eclipse.jface.viewers.StructuredViewer#doFindItem(java.lang.Object) >+ */ >+ protected Widget doFindItem(Object element) { >+ Widget node = (Widget) nodesMap.get(element); >+ Widget connection = (Widget) connectionsMap.get(element); >+ return (node != null) ? node : connection; >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see org.eclipse.jface.viewers.StructuredViewer#getSelectionFromWidget() >+ */ >+ protected List getSelectionFromWidget() { >+ List internalSelection = getWidgetSelection(); >+ LinkedList externalSelection = new LinkedList(); >+ for (Iterator i = internalSelection.iterator(); i.hasNext();) { >+ // @tag zest.todo : should there be a method on IGraphItem to get >+ // the external data? >+ GraphItem item = (GraphItem) i.next(); >+ if (item instanceof GraphNode) { >+ externalSelection.add(((GraphNode) item).getData()); >+ } else if (item instanceof GraphConnection) { >+ externalSelection.add(((GraphConnection) item) >+ .getExternalConnection()); >+ } else if (item instanceof Widget) { >+ externalSelection.add(((Widget) item).getData()); >+ } >+ } >+ return externalSelection; >+ } >+ >+ protected GraphItem[] /* GraphItem */findItems(List l) { >+ if (l == null) { >+ return new GraphItem[0]; >+ } >+ >+ ArrayList list = new ArrayList(); >+ Iterator iterator = l.iterator(); >+ >+ while (iterator.hasNext()) { >+ GraphItem w = (GraphItem) findItem(iterator.next()); >+ list.add(w); >+ } >+ return (GraphItem[]) list.toArray(new GraphItem[list.size()]); >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see >+ * org.eclipse.jface.viewers.StructuredViewer#setSelectionToWidget(java. >+ * util.List, boolean) >+ */ >+ protected void setSelectionToWidget(List l, boolean reveal) { >+ Graph control = (Graph) getControl(); >+ List selection = new LinkedList(); >+ for (Iterator i = l.iterator(); i.hasNext();) { >+ Object obj = i.next(); >+ GraphNode node = (GraphNode) nodesMap.get(obj); >+ GraphConnection conn = (GraphConnection) connectionsMap.get(obj); >+ if (node != null) { >+ selection.add(node); >+ } >+ if (conn != null) { >+ selection.add(conn); >+ } >+ } >+ control.setSelection((GraphNode[]) selection >+ .toArray(new GraphNode[selection.size()])); >+ } >+ >+ /** >+ * Gets the internal model elements that are selected. >+ * >+ * @return >+ */ >+ protected List getWidgetSelection() { >+ Graph control = (Graph) getControl(); >+ return control.getSelection(); >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see org.eclipse.jface.viewers.Viewer#inputChanged(java.lang.Object, >+ * java.lang.Object) >+ */ >+ protected void inputChanged(Object input, Object oldInput) { >+ IStylingGraphModelFactory factory = getFactory(); >+ factory.setConnectionStyle(getConnectionStyle()); >+ factory.setNodeStyle(getNodeStyle()); >+ >+ // Save the old map so we can set the size and position of any nodes >+ // that are the same >+ Map oldNodesMap = nodesMap; >+ Graph graph = (Graph) getControl(); >+ graph.setSelection(new GraphNode[0]); >+ >+ Iterator iterator = nodesMap.values().iterator(); >+ while (iterator.hasNext()) { >+ GraphNode node = (GraphNode) iterator.next(); >+ if (!node.isDisposed()) { >+ node.dispose(); >+ } >+ } >+ >+ iterator = connectionsMap.values().iterator(); >+ while (iterator.hasNext()) { >+ GraphConnection connection = (GraphConnection) iterator.next(); >+ if (!connection.isDisposed()) { >+ connection.dispose(); >+ } >+ } >+ >+ nodesMap = new HashMap(); >+ connectionsMap = new HashMap(); >+ >+ graph = factory.createGraphModel(graph); >+ >+ ((Graph) getControl()).setNodeStyle(getNodeStyle()); >+ ((Graph) getControl()).setConnectionStyle(getConnectionStyle()); >+ >+ // check if any of the pre-existing nodes are still present >+ // in this case we want them to keep the same location & size >+ for (Iterator iter = oldNodesMap.keySet().iterator(); iter.hasNext();) { >+ Object data = iter.next(); >+ GraphNode newNode = (GraphNode) nodesMap.get(data); >+ if (newNode != null) { >+ GraphNode oldNode = (GraphNode) oldNodesMap.get(data); >+ newNode.setLocation(oldNode.getLocation().x, oldNode >+ .getLocation().y); >+ if (oldNode.isSizeFixed()) { >+ newNode.setSize(oldNode.getSize().width, >+ oldNode.getSize().height); >+ } >+ } >+ } >+ } >+ >+ /** >+ * Returns the factory used to create the model. This must not be called >+ * before the content provider is set. >+ * >+ * @return >+ * @noreference This method is not intended to be referenced by clients. >+ * @nooverride This method is not intended to be re-implemented or extended >+ * by clients. >+ */ >+ protected abstract IStylingGraphModelFactory getFactory(); >+ >+ protected void filterVisuals() { >+ if (getGraphControl() == null) { >+ return; >+ } >+ Object[] filtered = getFilteredChildren(getInput()); >+ SimpleGraphComparator comparator = new SimpleGraphComparator(); >+ TreeSet filteredElements = new TreeSet(comparator); >+ TreeSet unfilteredElements = new TreeSet(comparator); >+ List connections = getGraphControl().getConnections(); >+ List nodes = getGraphControl().getNodes(); >+ if (filtered.length == 0) { >+ // set everything to invisible. >+ // @tag zest.bug.156528-Filters.check : should we only filter out >+ // the nodes? >+ for (Iterator i = connections.iterator(); i.hasNext();) { >+ GraphConnection c = (GraphConnection) i.next(); >+ c.setVisible(false); >+ } >+ for (Iterator i = nodes.iterator(); i.hasNext();) { >+ GraphNode n = (GraphNode) i.next(); >+ n.setVisible(false); >+ } >+ return; >+ } >+ for (Iterator i = connections.iterator(); i.hasNext();) { >+ GraphConnection c = (GraphConnection) i.next(); >+ if (c.getExternalConnection() != null) { >+ unfilteredElements.add(c); >+ } >+ } >+ for (Iterator i = nodes.iterator(); i.hasNext();) { >+ GraphNode n = (GraphNode) i.next(); >+ if (n.getData() != null) { >+ unfilteredElements.add(n); >+ } >+ } >+ for (int i = 0; i < filtered.length; i++) { >+ Object modelElement = connectionsMap.get(filtered[i]); >+ if (modelElement == null) { >+ modelElement = nodesMap.get(filtered[i]); >+ } >+ if (modelElement != null) { >+ filteredElements.add(modelElement); >+ } >+ } >+ unfilteredElements.removeAll(filteredElements); >+ // set all the elements that did not pass the filters to invisible, and >+ // all the elements that passed to visible. >+ while (unfilteredElements.size() > 0) { >+ GraphItem i = (GraphItem) unfilteredElements.first(); >+ i.setVisible(false); >+ unfilteredElements.remove(i); >+ } >+ while (filteredElements.size() > 0) { >+ GraphItem i = (GraphItem) filteredElements.first(); >+ i.setVisible(true); >+ filteredElements.remove(i); >+ } >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see >+ * org.eclipse.jface.viewers.StructuredViewer#getRawChildren(java.lang.Object >+ * ) >+ */ >+ protected Object[] getRawChildren(Object parent) { >+ if (parent == getInput()) { >+ // get the children from the model. >+ LinkedList children = new LinkedList(); >+ if (getGraphControl() != null) { >+ List connections = getGraphControl().getConnections(); >+ List nodes = getGraphControl().getNodes(); >+ for (Iterator i = connections.iterator(); i.hasNext();) { >+ GraphConnection c = (GraphConnection) i.next(); >+ if (c.getExternalConnection() != null) { >+ children.add(c.getExternalConnection()); >+ } >+ } >+ for (Iterator i = nodes.iterator(); i.hasNext();) { >+ GraphNode n = (GraphNode) i.next(); >+ if (n.getData() != null) { >+ children.add(n.getData()); >+ } >+ } >+ return children.toArray(); >+ } >+ } >+ return super.getRawChildren(parent); >+ } >+ >+ /** >+ * >+ */ >+ public void reveal(Object element) { >+ Widget[] items = this.findItems(element); >+ for (int i = 0; i < items.length; i++) { >+ Widget item = items[i]; >+ if (item instanceof GraphNode) { >+ GraphNode graphModelNode = (GraphNode) item; >+ graphModelNode.highlight(); >+ } else if (item instanceof GraphConnection) { >+ GraphConnection graphModelConnection = (GraphConnection) item; >+ graphModelConnection.highlight(); >+ } >+ } >+ } >+ >+ public void unReveal(Object element) { >+ Widget[] items = this.findItems(element); >+ for (int i = 0; i < items.length; i++) { >+ Widget item = items[i]; >+ if (item instanceof GraphNode) { >+ GraphNode graphModelNode = (GraphNode) item; >+ graphModelNode.unhighlight(); >+ } else if (item instanceof GraphConnection) { >+ GraphConnection graphModelConnection = (GraphConnection) item; >+ graphModelConnection.unhighlight(); >+ } >+ } >+ } >+ >+ /** >+ * Applies the viewers layouts. >+ * >+ */ >+ public abstract void applyLayout(); >+ >+ /** >+ * Removes the given connection object from the layout algorithm and the >+ * model. >+ * >+ * @param connection >+ */ >+ public void removeRelationship(Object connection) { >+ GraphConnection relationship = (GraphConnection) connectionsMap >+ .get(connection); >+ >+ if (relationship != null) { >+ // remove the relationship from the model >+ relationship.dispose(); >+ } >+ } >+ >+ /** >+ * Creates a new node and adds it to the graph. If it already exists nothing >+ * happens. >+ * >+ * @param newNode >+ */ >+ public void addNode(Object element) { >+ if (nodesMap.get(element) == null) { >+ // create the new node >+ getFactory().createNode(getGraphControl(), element); >+ >+ } >+ } >+ >+ /** >+ * Removes the given element from the layout algorithm and the model. >+ * >+ * @param element >+ * The node element to remove. >+ */ >+ public void removeNode(Object element) { >+ GraphNode node = (GraphNode) nodesMap.get(element); >+ >+ if (node != null) { >+ // remove the node and it's connections from the model >+ node.dispose(); >+ } >+ } >+ >+ /** >+ * Creates a new relationship between the source node and the destination >+ * node. If either node doesn't exist then it will be created. >+ * >+ * @param connection >+ * The connection data object. >+ * @param srcNode >+ * The source node data object. >+ * @param destNode >+ * The destination node data object. >+ */ >+ public void addRelationship(Object connection, Object srcNode, >+ Object destNode) { >+ // create the new relationship >+ IStylingGraphModelFactory modelFactory = getFactory(); >+ modelFactory.createConnection(getGraphControl(), connection, srcNode, >+ destNode); >+ >+ } >+ >+ /** >+ * Adds a new relationship given the connection. It will use the content >+ * provider to determine the source and destination nodes. >+ * >+ * @param connection >+ * The connection data object. >+ */ >+ public void addRelationship(Object connection) { >+ IStylingGraphModelFactory modelFactory = getFactory(); >+ if (connectionsMap.get(connection) == null) { >+ if (modelFactory.getContentProvider() instanceof IGraphContentProvider) { >+ IGraphContentProvider content = ((IGraphContentProvider) modelFactory >+ .getContentProvider()); >+ Object source = content.getSource(connection); >+ Object dest = content.getDestination(connection); >+ // create the new relationship >+ modelFactory.createConnection(getGraphControl(), connection, >+ source, dest); >+ } else { >+ throw new UnsupportedOperationException(); >+ } >+ } >+ } >+ >+ /** >+ * Converts the list of GraphModelConnection objects into an array and >+ * returns it. >+ * >+ * @return GraphModelConnection[] >+ */ >+ protected GraphConnection[] getConnectionsArray(Graph graph) { >+ GraphConnection[] connsArray = new GraphConnection[graph >+ .getConnections().size()]; >+ connsArray = (GraphConnection[]) graph.getConnections().toArray( >+ connsArray); >+ return connsArray; >+ } >+ >+ /** >+ * Converts the list of GraphModelNode objects into an array an returns it. >+ * >+ * @return GraphModelNode[] >+ */ >+ protected GraphNode[] getNodesArray(Graph graph) { >+ GraphNode[] nodesArray = new GraphNode[graph.getNodes().size()]; >+ nodesArray = (GraphNode[]) graph.getNodes().toArray(nodesArray); >+ return nodesArray; >+ } >+ >+} >Index: src/org/eclipse/zest/core/viewers/AbstractZoomableViewer.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/viewers/AbstractZoomableViewer.java,v >retrieving revision 1.5 >diff -u -r1.5 AbstractZoomableViewer.java >--- src/org/eclipse/zest/core/viewers/AbstractZoomableViewer.java 12 Sep 2007 20:44:39 -0000 1.5 >+++ src/org/eclipse/zest/core/viewers/AbstractZoomableViewer.java 15 Mar 2010 03:48:53 -0000 >@@ -1,39 +1,43 @@ >-/******************************************************************************* >- * Copyright 2005-2006, 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; >- >-import org.eclipse.draw2d.geometry.Rectangle; >-import org.eclipse.jface.viewers.StructuredViewer; >-import org.eclipse.zest.core.viewers.internal.ZoomManager; >- >-/** >- * A simple interface that provides zooming capabilites. Not intended to be subclassed by clients. >- * @author Del Myers >- * >- */ >-//@tag bug.156286-Zooming.fix >-public abstract class AbstractZoomableViewer extends StructuredViewer { >- /** >- * Returns a ZoomManager that zooming can be done on. May return null if none >- * is available. >- * @return a ZoomManager that zooming can be done on. >- */ >- protected abstract ZoomManager getZoomManager(); >- >- public void zoomTo(int x, int y, int width, int height) { >- Rectangle r = new Rectangle(x,y,width,height); >- if (r.isEmpty()) { >- getZoomManager().setZoomAsText("100%"); >- } else { >- getZoomManager().zoomTo(r); >- } >- } >-} >+/******************************************************************************* >+ * Copyright 2005-2010, 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; >+ >+import org.eclipse.draw2d.geometry.Rectangle; >+import org.eclipse.jface.viewers.StructuredViewer; >+ >+/** >+ * A simple interface that provides zooming capabilites. Not intended to be >+ * subclassed by clients. >+ * >+ * @author Del Myers >+ * >+ * @noextend This class is not intended to be subclassed by clients. >+ * >+ */ >+// @tag bug.156286-Zooming.fix >+public abstract class AbstractZoomableViewer extends StructuredViewer { >+ /** >+ * Returns a ZoomManager that zooming can be done on. May return null if >+ * none is available. >+ * >+ * @return a ZoomManager that zooming can be done on. >+ * @since 2.0 >+ */ >+ protected abstract ZoomManager getZoomManager(); >+ >+ public void zoomTo(int x, int y, int width, int height) { >+ Rectangle r = new Rectangle(x, y, width, height); >+ if (r.isEmpty()) { >+ getZoomManager().setZoomAsText("100%"); >+ } else { >+ getZoomManager().zoomTo(r); >+ } >+ } >+} >Index: src/org/eclipse/zest/core/viewers/EntityConnectionData.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/viewers/EntityConnectionData.java,v >retrieving revision 1.6 >diff -u -r1.6 EntityConnectionData.java >--- src/org/eclipse/zest/core/viewers/EntityConnectionData.java 12 Sep 2007 20:44:39 -0000 1.6 >+++ src/org/eclipse/zest/core/viewers/EntityConnectionData.java 15 Mar 2010 03:48:53 -0000 >@@ -1,59 +1,61 @@ >-/******************************************************************************* >- * Copyright 2005-2006, 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; >- >-/** >- * A simple object that is used as the "external connection" in content providers that don't >- * ask the user to create their own external connection. >- * >- * This is used whenever users don't specify a connection >- * >- * @author Del Myers >- */ >-public final class EntityConnectionData { >- public final Object source; >- public final Object dest; >- >- /** >- * Creates a new entity connection data. The source and dest >- * are users nodes. >- */ >- public EntityConnectionData(Object source, Object dest) { >- /* >- if (source == null) { >- throw new RuntimeException("Creating relationship with null source object"); >- } >- if (dest == null) { >- throw new RuntimeException("Creating relationship with null dest object"); >- } >- */ >- this.source = source; >- this.dest = dest; >- } >- >- /* (non-Javadoc) >- * @see java.lang.Object#equals(java.lang.Object) >- */ >- public boolean equals(Object obj) { >- if (!(obj instanceof EntityConnectionData)) { >- return false; >- } >- EntityConnectionData that = (EntityConnectionData) obj; >- return (this.source.equals(that.source) && this.dest.equals(that.dest)); >- } >- >- /* (non-Javadoc) >- * @see java.lang.Object#hashCode() >- */ >- public int hashCode() { >- return this.source.hashCode() + this.dest.hashCode(); >- } >-} >+/******************************************************************************* >+ * Copyright 2005-2010, 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; >+ >+/** >+ * A simple object that is used as the "external connection" in content >+ * providers that don't ask the user to create their own external connection. >+ * >+ * This is used whenever users don't specify a connection >+ * >+ * @author Del Myers >+ */ >+public final class EntityConnectionData { >+ public final Object source; >+ public final Object dest; >+ >+ /** >+ * Creates a new entity connection data. The source and dest are users >+ * nodes. >+ */ >+ public EntityConnectionData(Object source, Object dest) { >+ /* >+ * if (source == null) { throw new >+ * RuntimeException("Creating relationship with null source object"); } >+ * if (dest == null) { throw new >+ * RuntimeException("Creating relationship with null dest object"); } >+ */ >+ this.source = source; >+ this.dest = dest; >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see java.lang.Object#equals(java.lang.Object) >+ */ >+ public boolean equals(Object obj) { >+ if (!(obj instanceof EntityConnectionData)) { >+ return false; >+ } >+ EntityConnectionData that = (EntityConnectionData) obj; >+ return (this.source.equals(that.source) && this.dest.equals(that.dest)); >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see java.lang.Object#hashCode() >+ */ >+ public int hashCode() { >+ return this.source.hashCode() + this.dest.hashCode(); >+ } >+} >Index: src/org/eclipse/zest/core/viewers/GraphViewer.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/viewers/GraphViewer.java,v >retrieving revision 1.7 >diff -u -r1.7 GraphViewer.java >--- src/org/eclipse/zest/core/viewers/GraphViewer.java 14 Oct 2008 19:57:32 -0000 1.7 >+++ src/org/eclipse/zest/core/viewers/GraphViewer.java 15 Mar 2010 03:48:53 -0000 >@@ -1,300 +1,380 @@ >-/******************************************************************************* >- * Copyright 2005, 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; >- >-import java.util.ArrayList; >-import java.util.Iterator; >-import java.util.List; >- >-import org.eclipse.jface.viewers.DoubleClickEvent; >-import org.eclipse.jface.viewers.IContentProvider; >-import org.eclipse.jface.viewers.ISelection; >-import org.eclipse.jface.viewers.ISelectionChangedListener; >-import org.eclipse.jface.viewers.ISelectionProvider; >-import org.eclipse.jface.viewers.SelectionChangedEvent; >-import org.eclipse.swt.SWT; >-import org.eclipse.swt.events.MouseEvent; >-import org.eclipse.swt.events.MouseListener; >-import org.eclipse.swt.events.SelectionAdapter; >-import org.eclipse.swt.events.SelectionEvent; >-import org.eclipse.swt.widgets.Composite; >-import org.eclipse.swt.widgets.Control; >-import org.eclipse.swt.widgets.Widget; >-import org.eclipse.zest.core.viewers.internal.AbstractStructuredGraphViewer; >-import org.eclipse.zest.core.viewers.internal.GraphModelEntityFactory; >-import org.eclipse.zest.core.viewers.internal.GraphModelEntityRelationshipFactory; >-import org.eclipse.zest.core.viewers.internal.GraphModelFactory; >-import org.eclipse.zest.core.viewers.internal.IStylingGraphModelFactory; >-import org.eclipse.zest.core.viewers.internal.ZoomManager; >-import org.eclipse.zest.core.widgets.Graph; >-import org.eclipse.zest.core.widgets.GraphItem; >-import org.eclipse.zest.core.widgets.ZestStyles; >-import org.eclipse.zest.layouts.LayoutAlgorithm; >- >-/* >- * This view is used to represent a static graph. Static graphs can be layed >- * out, but do not continually update their layout locations. >- * >- * @author Ian Bull >- * >- * @author Chris Callendar >- */ >-public class GraphViewer extends AbstractStructuredGraphViewer implements ISelectionProvider { >- >- protected Graph graph = null; >- private IStylingGraphModelFactory modelFactory = null; >- private List selectionChangedListeners = null; >- ZoomManager zoomManager = null; >- >- /** >- * Initializes the viewer. >- * >- * @param composite >- * @param style >- * the style for the viewer and for the layout algorithm >- * @see ZestStyles#LAYOUT_GRID >- * @see ZestStyles#LAYOUT_TREE >- * @see ZestStyles#LAYOUT_RADIAL >- * @see ZestStyles#LAYOUT_SPRING >- * @see ZestStyles#NO_OVERLAPPING_NODES >- * @see ZestStyles#NODES_HIGHLIGHT_ADJACENT >- * @see SWT#V_SCROLL >- * @see SWT#H_SCROLL >- */ >- public GraphViewer(Composite composite, int style) { >- super(style); >- this.graph = new Graph(composite, style); >- hookControl(this.graph); >- } >- >- public void setControl(Graph graphModel) { >- this.graph = graphModel; >- hookControl(this.graph); >- } >- >- protected void hookControl(Control control) { >- super.hookControl(control); >- >- selectionChangedListeners = new ArrayList(); >- getGraphControl().addSelectionListener(new SelectionAdapter() { >- >- public void widgetSelected(SelectionEvent e) { >- Iterator iterator = selectionChangedListeners.iterator(); >- >- ISelection structuredSelection = getSelection(); >- SelectionChangedEvent event = new SelectionChangedEvent(GraphViewer.this, structuredSelection); >- >- while (iterator.hasNext()) { >- ISelectionChangedListener listener = (ISelectionChangedListener) iterator.next(); >- listener.selectionChanged(event); >- } >- >- } >- >- }); >- >- control.addMouseListener(new MouseListener() { >- >- public void mouseDoubleClick(MouseEvent e) { >- DoubleClickEvent doubleClickEvent = new DoubleClickEvent(GraphViewer.this, getSelection()); >- fireDoubleClick(doubleClickEvent); >- } >- >- public void mouseDown(MouseEvent e) { >- >- } >- >- public void mouseUp(MouseEvent e) { >- >- } >- >- }); >- } >- >- /** >- * Gets the styles for this structuredViewer >- * >- * @return >- */ >- public int getStyle() { >- return this.graph.getStyle(); >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.zest.core.viewer.internal.AbstractStructuredGraphViewer#getGraphControl() >- */ >- public Graph getGraphControl() { >- return super.getGraphControl(); >- }; >- >- /** >- * Sets the layout algorithm to use for this viewer. >- * >- * @param algorithm >- * the algorithm to layout the nodes >- * @param runLayout >- * if the layout should be run >- */ >- public void setLayoutAlgorithm(LayoutAlgorithm algorithm, boolean runLayout) { >- graph.setLayoutAlgorithm(algorithm, runLayout); >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.zest.core.viewer.internal.AbstractStructuredGraphViewer#setLayoutAlgorithm(org.eclipse.zest.layouts.LayoutAlgorithm) >- */ >- public void setLayoutAlgorithm(LayoutAlgorithm algorithm) { >- super.setLayoutAlgorithm(algorithm); >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.zest.core.viewers.AbstractStructuredGraphViewer#setNodeStyle(int) >- */ >- public void setNodeStyle(int nodeStyle) { >- super.setNodeStyle(nodeStyle); >- this.graph.setNodeStyle(nodeStyle); >- } >- >- public void setContentProvider(IContentProvider contentProvider) { >- if (contentProvider instanceof IGraphContentProvider) { >- super.setContentProvider(contentProvider); >- } else if (contentProvider instanceof IGraphEntityContentProvider) { >- super.setContentProvider(contentProvider); >- } else if (contentProvider instanceof IGraphEntityRelationshipContentProvider) { >- super.setContentProvider(contentProvider); >- } else { >- throw new IllegalArgumentException("Invalid content provider, only IGraphContentProvider, IGraphEntityContentProvider, or IGraphEntityRelationshipContentProvider are supported."); >- } >- } >- >- /** >- * Finds the graph widget item for a given user model item. >- * >- * Note: This method returns an internal interface (GraphItem). You should >- * be able to cast this to either a IGraphModelNode or IGraphModelConnection >- * (which are also internal). These are internal because this API is not >- * stable. If use this method (to access internal nodes and edges), your >- * code may not compile between versions. >- * >- * @param The >- * user model node. >- * @return An IGraphItem. This should be either a IGraphModelNode or >- * IGraphModelConnection >- */ >- public GraphItem findGraphItem(Object element) { >- Widget[] result = findItems(element); >- return (result.length == 0 || !(result[0] instanceof GraphItem)) ? null : (GraphItem) result[0]; >- } >- >- /** >- * Applys the current layout to the viewer >- */ >- public void applyLayout() { >- graph.applyLayout(); >- } >- >- protected void setSelectionToWidget(List l, boolean reveal) { >- GraphItem[] listOfItems = findItems(l); >- graph.setSelection(listOfItems); >- } >- >- public Control getControl() { >- return graph; >- } >- >- public Object[] getNodeElements() { >- return super.getNodeElements(); >- } >- >- public Object[] getConnectionElements() { >- return super.getConnectionElements(); >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.zest.core.viewer.internal.AbstractStructuredGraphViewer#reveal(java.lang.Object) >- */ >- public void reveal(Object element) { >- super.reveal(element); >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.zest.core.viewer.internal.AbstractStructuredGraphViewer#setConnectionStyle(int) >- */ >- public void setConnectionStyle(int connectionStyle) { >- super.setConnectionStyle(connectionStyle); >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.zest.core.viewer.internal.AbstractStructuredGraphViewer#unReveal(java.lang.Object) >- */ >- public void unReveal(Object element) { >- super.unReveal(element); >- } >- >- public void addSelectionChangedListener(ISelectionChangedListener listener) { >- if (!selectionChangedListeners.contains(listener)) { >- selectionChangedListeners.add(listener); >- } >- } >- >- public void removeSelectionChangedListener(ISelectionChangedListener listener) { >- if (selectionChangedListeners.contains(listener)) { >- selectionChangedListeners.remove(listener); >- } >- } >- >- // @tag zest.bug.156286-Zooming.fix.experimental : expose the zoom manager >- // for new actions. >- protected ZoomManager getZoomManager() { >- if (zoomManager == null) { >- zoomManager = new ZoomManager(getGraphControl().getRootLayer(), getGraphControl().getViewport()); >- } >- return zoomManager; >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.zest.core.viewers.AbstractStructuredGraphViewer#getFactory() >- */ >- protected IStylingGraphModelFactory getFactory() { >- if (modelFactory == null) { >- if (getContentProvider() instanceof IGraphContentProvider) { >- modelFactory = new GraphModelFactory(this); >- } else if (getContentProvider() instanceof IGraphEntityContentProvider) { >- modelFactory = new GraphModelEntityFactory(this); >- } else if (getContentProvider() instanceof IGraphEntityRelationshipContentProvider) { >- modelFactory = new GraphModelEntityRelationshipFactory(this); >- } >- } >- return modelFactory; >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.zest.core.viewers.AbstractStructuredGraphViewer#getLayoutAlgorithm() >- */ >- protected LayoutAlgorithm getLayoutAlgorithm() { >- return graph.getLayoutAlgorithm(); >- } >- >-} >+/******************************************************************************* >+ * Copyright 2005-2010, 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; >+ >+import java.util.ArrayList; >+import java.util.Iterator; >+import java.util.List; >+ >+import org.eclipse.jface.viewers.DoubleClickEvent; >+import org.eclipse.jface.viewers.IContentProvider; >+import org.eclipse.jface.viewers.ISelection; >+import org.eclipse.jface.viewers.ISelectionChangedListener; >+import org.eclipse.jface.viewers.ISelectionProvider; >+import org.eclipse.jface.viewers.SelectionChangedEvent; >+import org.eclipse.swt.SWT; >+import org.eclipse.swt.events.MouseEvent; >+import org.eclipse.swt.events.MouseListener; >+import org.eclipse.swt.events.SelectionAdapter; >+import org.eclipse.swt.events.SelectionEvent; >+import org.eclipse.swt.widgets.Composite; >+import org.eclipse.swt.widgets.Control; >+import org.eclipse.swt.widgets.Widget; >+import org.eclipse.zest.core.viewers.internal.GraphModelEntityFactory; >+import org.eclipse.zest.core.viewers.internal.GraphModelEntityRelationshipFactory; >+import org.eclipse.zest.core.viewers.internal.GraphModelFactory; >+import org.eclipse.zest.core.viewers.internal.IStylingGraphModelFactory; >+import org.eclipse.zest.core.widgets.Graph; >+import org.eclipse.zest.core.widgets.GraphItem; >+import org.eclipse.zest.core.widgets.ZestStyles; >+import org.eclipse.zest.layouts.LayoutAlgorithm; >+ >+/** >+ * This view is used to represent a static graph. Static graphs can be layed >+ * out, but do not continually update their layout locations. >+ * >+ * @author Ian Bull >+ * >+ * @author Chris Callendar >+ * >+ * @noextend This class is not intended to be subclassed by clients. >+ */ >+public class GraphViewer extends AbstractStructuredGraphViewer implements >+ ISelectionProvider { >+ >+ protected Graph graph = null; >+ private IStylingGraphModelFactory modelFactory = null; >+ private List selectionChangedListeners = null; >+ ZoomManager zoomManager = null; >+ >+ /** >+ * Initializes the viewer. >+ * >+ * @param composite >+ * @param style >+ * the style for the viewer and for the layout algorithm >+ * @see ZestStyles#LAYOUT_GRID >+ * @see ZestStyles#LAYOUT_TREE >+ * @see ZestStyles#LAYOUT_RADIAL >+ * @see ZestStyles#LAYOUT_SPRING >+ * @see ZestStyles#NO_OVERLAPPING_NODES >+ * @see ZestStyles#NODES_HIGHLIGHT_ADJACENT >+ * @see SWT#V_SCROLL >+ * @see SWT#H_SCROLL >+ */ >+ public GraphViewer(Composite composite, int style) { >+ super(style); >+ this.graph = new Graph(composite, style); >+ hookControl(this.graph); >+ } >+ >+ public void setControl(Graph graphModel) { >+ this.graph = graphModel; >+ hookControl(this.graph); >+ } >+ >+ protected void hookControl(Control control) { >+ super.hookControl(control); >+ >+ selectionChangedListeners = new ArrayList(); >+ getGraphControl().addSelectionListener(new SelectionAdapter() { >+ >+ public void widgetSelected(SelectionEvent e) { >+ Iterator iterator = selectionChangedListeners.iterator(); >+ >+ ISelection structuredSelection = getSelection(); >+ SelectionChangedEvent event = new SelectionChangedEvent( >+ GraphViewer.this, structuredSelection); >+ >+ while (iterator.hasNext()) { >+ ISelectionChangedListener listener = (ISelectionChangedListener) iterator >+ .next(); >+ listener.selectionChanged(event); >+ } >+ >+ } >+ >+ }); >+ >+ control.addMouseListener(new MouseListener() { >+ >+ public void mouseDoubleClick(MouseEvent e) { >+ DoubleClickEvent doubleClickEvent = new DoubleClickEvent( >+ GraphViewer.this, getSelection()); >+ fireDoubleClick(doubleClickEvent); >+ } >+ >+ public void mouseDown(MouseEvent e) { >+ >+ } >+ >+ public void mouseUp(MouseEvent e) { >+ >+ } >+ >+ }); >+ } >+ >+ protected void inputChanged(Object input, Object oldInput) { >+ graph.setDynamicLayout(false); >+ super.inputChanged(input, oldInput); >+ graph.setDynamicLayout(true); >+ graph.applyLayout(); >+ } >+ >+ /** >+ * Gets the styles for this structuredViewer >+ * >+ * @return >+ */ >+ public int getStyle() { >+ return this.graph.getStyle(); >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @seeorg.eclipse.zest.core.viewer.internal.AbstractStructuredGraphViewer# >+ * getGraphControl() >+ */ >+ public Graph getGraphControl() { >+ return super.getGraphControl(); >+ }; >+ >+ /** >+ * Sets the layout algorithm to use for this viewer. >+ * >+ * @param algorithm >+ * the algorithm to layout the nodes >+ * @param runLayout >+ * if the layout should be run >+ */ >+ public void setLayoutAlgorithm(LayoutAlgorithm algorithm, boolean runLayout) { >+ graph.setLayoutAlgorithm(algorithm, runLayout); >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @seeorg.eclipse.zest.core.viewer.internal.AbstractStructuredGraphViewer# >+ * setLayoutAlgorithm(org.eclipse.zest.layouts.LayoutAlgorithm) >+ */ >+ public void setLayoutAlgorithm(LayoutAlgorithm algorithm) { >+ super.setLayoutAlgorithm(algorithm); >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see >+ * org.eclipse.zest.core.viewers.AbstractStructuredGraphViewer#setNodeStyle >+ * (int) >+ */ >+ public void setNodeStyle(int nodeStyle) { >+ super.setNodeStyle(nodeStyle); >+ this.graph.setNodeStyle(nodeStyle); >+ } >+ >+ public void setContentProvider(IContentProvider contentProvider) { >+ if (contentProvider instanceof IGraphContentProvider) { >+ super.setContentProvider(contentProvider); >+ } else if (contentProvider instanceof IGraphEntityContentProvider) { >+ super.setContentProvider(contentProvider); >+ } else if (contentProvider instanceof IGraphEntityRelationshipContentProvider) { >+ super.setContentProvider(contentProvider); >+ } else { >+ throw new IllegalArgumentException( >+ "Invalid content provider, only IGraphContentProvider, IGraphEntityContentProvider, or IGraphEntityRelationshipContentProvider are supported."); >+ } >+ } >+ >+ /** >+ * Finds the graph widget item for a given user model item. >+ * >+ * Note: This method returns an internal interface (GraphItem). You should >+ * be able to cast this to either a IGraphModelNode or IGraphModelConnection >+ * (which are also internal). These are internal because this API is not >+ * stable. If use this method (to access internal nodes and edges), your >+ * code may not compile between versions. >+ * >+ * @param The >+ * user model node. >+ * @return An IGraphItem. This should be either a IGraphModelNode or >+ * IGraphModelConnection >+ */ >+ public GraphItem findGraphItem(Object element) { >+ Widget[] result = findItems(element); >+ return (result.length == 0 || !(result[0] instanceof GraphItem)) ? null >+ : (GraphItem) result[0]; >+ } >+ >+ /** >+ * Applys the current layout to the viewer >+ */ >+ public void applyLayout() { >+ graph.applyLayout(); >+ } >+ >+ protected void setSelectionToWidget(List l, boolean reveal) { >+ GraphItem[] listOfItems = findItems(l); >+ graph.setSelection(listOfItems); >+ } >+ >+ public Control getControl() { >+ return graph; >+ } >+ >+ public Object[] getNodeElements() { >+ return super.getNodeElements(); >+ } >+ >+ public Object[] getConnectionElements() { >+ return super.getConnectionElements(); >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see >+ * org.eclipse.zest.core.viewer.internal.AbstractStructuredGraphViewer#reveal >+ * (java.lang.Object) >+ */ >+ public void reveal(Object element) { >+ super.reveal(element); >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @seeorg.eclipse.zest.core.viewer.internal.AbstractStructuredGraphViewer# >+ * setConnectionStyle(int) >+ */ >+ public void setConnectionStyle(int connectionStyle) { >+ super.setConnectionStyle(connectionStyle); >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see >+ * org.eclipse.zest.core.viewer.internal.AbstractStructuredGraphViewer#unReveal >+ * (java.lang.Object) >+ */ >+ public void unReveal(Object element) { >+ super.unReveal(element); >+ } >+ >+ public void addSelectionChangedListener(ISelectionChangedListener listener) { >+ if (!selectionChangedListeners.contains(listener)) { >+ selectionChangedListeners.add(listener); >+ } >+ } >+ >+ public void removeSelectionChangedListener( >+ ISelectionChangedListener listener) { >+ if (selectionChangedListeners.contains(listener)) { >+ selectionChangedListeners.remove(listener); >+ } >+ } >+ >+ /** >+ * {@inheritDoc} >+ * >+ * NOTE: If a layout algorithm is set in the receiver, layout is performed >+ * after the refresh. >+ */ >+ public void refresh(Object element) { >+ boolean dynamicLayoutEnabled = graph.isDynamicLayoutEnabled(); >+ graph.setDynamicLayout(false); >+ super.refresh(element); >+ graph.setDynamicLayout(dynamicLayoutEnabled); >+ } >+ >+ /** >+ * {@inheritDoc} >+ * >+ * NOTE: If a layout algorithm is set in the receiver, layout is performed >+ * after the refresh. >+ */ >+ public void refresh(Object element, boolean updateLabels) { >+ boolean dynamicLayoutEnabled = graph.isDynamicLayoutEnabled(); >+ graph.setDynamicLayout(false); >+ super.refresh(element, updateLabels); >+ graph.setDynamicLayout(dynamicLayoutEnabled); >+ } >+ >+ /** >+ * {@inheritDoc} >+ * >+ * NOTE: If a layout algorithm is set in the receiver, layout is performed >+ * after the update. >+ */ >+ public void update(Object element, String[] properties) { >+ boolean dynamicLayoutEnabled = graph.isDynamicLayoutEnabled(); >+ graph.setDynamicLayout(false); >+ super.update(element, properties); >+ graph.setDynamicLayout(dynamicLayoutEnabled); >+ } >+ >+ /** >+ * {@inheritDoc} >+ * >+ * NOTE: If a layout algorithm is set in the receiver, layout is performed >+ * after the update. >+ */ >+ public void update(Object[] elements, String[] properties) { >+ boolean dynamicLayoutEnabled = graph.isDynamicLayoutEnabled(); >+ graph.setDynamicLayout(false); >+ super.update(elements, properties); >+ graph.setDynamicLayout(dynamicLayoutEnabled); >+ } >+ >+ // @tag zest.bug.156286-Zooming.fix.experimental : expose the zoom manager >+ // for new actions. >+ protected ZoomManager getZoomManager() { >+ if (zoomManager == null) { >+ zoomManager = new ZoomManager(getGraphControl().getRootLayer(), >+ getGraphControl().getViewport()); >+ } >+ return zoomManager; >+ } >+ >+ /** >+ * (non-Javadoc) >+ * >+ * @see org.eclipse.zest.core.viewers.AbstractStructuredGraphViewer#getFactory() >+ * @noreference This method is not intended to be referenced by clients. >+ * @nooverride This method is not intended to be re-implemented or extended >+ * by clients. >+ */ >+ protected IStylingGraphModelFactory getFactory() { >+ if (modelFactory == null) { >+ if (getContentProvider() instanceof IGraphContentProvider) { >+ modelFactory = new GraphModelFactory(this); >+ } else if (getContentProvider() instanceof IGraphEntityContentProvider) { >+ modelFactory = new GraphModelEntityFactory(this); >+ } else if (getContentProvider() instanceof IGraphEntityRelationshipContentProvider) { >+ modelFactory = new GraphModelEntityRelationshipFactory(this); >+ } >+ } >+ return modelFactory; >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @seeorg.eclipse.zest.core.viewers.AbstractStructuredGraphViewer# >+ * getLayoutAlgorithm() >+ */ >+ protected LayoutAlgorithm getLayoutAlgorithm() { >+ return graph.getLayoutAlgorithm(); >+ } >+ >+} >Index: src/org/eclipse/zest/core/viewers/IConnectionStyleBezierExtension.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/viewers/IConnectionStyleBezierExtension.java,v >retrieving revision 1.3 >diff -u -r1.3 IConnectionStyleBezierExtension.java >--- src/org/eclipse/zest/core/viewers/IConnectionStyleBezierExtension.java 12 Sep 2007 20:44:39 -0000 1.3 >+++ src/org/eclipse/zest/core/viewers/IConnectionStyleBezierExtension.java 15 Mar 2010 03:48:53 -0000 >@@ -1,67 +1,76 @@ >-/******************************************************************************* >- * Copyright 2005-2006, 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; >- >-/** >- * An extension to the IConnectinStyleProvider that allows styling specific to >- * bezier curves. >- * >- * Bezier curves are defined by a set of four points: two point in the layout >- * (start and end), and two related control points (also start and end). The >- * control points are defined relative to their corresponding layout point. >- * This definition includes an angle between the layout point and the line >- * between the two layout points, as well as a ratio distance from the corresponding >- * layout point. The ratio distance is defined as a fraction between 0 and 1 >- * of the distance between the two layout points. Using this definition >- * allows bezier curves to have a consistant look regardless of the actual >- * positions of the nodes in the layouts. >- * @author Del Myers >- * >- */ >-//@tag bug(152530-Bezier(fix)) : users can style bezier curves. >-public interface IConnectionStyleBezierExtension { >- >- /** >- * Gets the angle between the start point, and the line between the start >- * and end, which will define the position of the start control point. >- * If the start angle, and the end angle are the same sign, the two control >- * points are guaranteed to be on the same side of the line. >- * @param rel the relationship to base on. >- * @return the start angle or <code>Double.NaN</code> for defaults. >- */ >- double getStartAngle(Object rel); >- >- /** >- * Gets the angle between the end point, and the line between the start >- * and end, which will define the position of the end control point. >- * If the start angle, and the end angle are the same sign, the two control >- * points are guaranteed to be on the same side of the line. >- * @param rel the relationship to base on. >- * @return the end angle or <code>Double.NaN</code> for defaults. >- */ >- double getEndAngle(Object rel); >- >- /** >- * Gets the distance between the start point and the start control point, >- * as a fraction of the distance between the start point and end point. >- * @param rel the relationship to base on. >- * @return the start distance or <code>Double.NaN</code> for defaults. >- */ >- double getStartDistance(Object rel); >- >- /** >- * Gets the distance between the end point and the end control point, >- * as a fraction of the distance between the start point and end point. >- * @param rel the relationship to base on. >- * @return the end distance or <code>Double.NaN</code> for defaults. >- */ >- double getEndDistance(Object rel); >-} >+/******************************************************************************* >+ * Copyright 2005-2010, 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; >+ >+/** >+ * An extension to the IConnectinStyleProvider that allows styling specific to >+ * bezier curves. >+ * >+ * Bezier curves are defined by a set of four points: two point in the layout >+ * (start and end), and two related control points (also start and end). The >+ * control points are defined relative to their corresponding layout point. This >+ * definition includes an angle between the layout point and the line between >+ * the two layout points, as well as a ratio distance from the corresponding >+ * layout point. The ratio distance is defined as a fraction between 0 and 1 of >+ * the distance between the two layout points. Using this definition allows >+ * bezier curves to have a consistant look regardless of the actual positions of >+ * the nodes in the layouts. >+ * >+ * @author Del Myers >+ * >+ */ >+// @tag bug(152530-Bezier(fix)) : users can style bezier curves. >+public interface IConnectionStyleBezierExtension { >+ >+ /** >+ * Gets the angle between the start point, and the line between the start >+ * and end, which will define the position of the start control point. If >+ * the start angle, and the end angle are the same sign, the two control >+ * points are guaranteed to be on the same side of the line. >+ * >+ * @param rel >+ * the relationship to base on. >+ * @return the start angle or <code>Double.NaN</code> for defaults. >+ */ >+ double getStartAngle(Object rel); >+ >+ /** >+ * Gets the angle between the end point, and the line between the start and >+ * end, which will define the position of the end control point. If the >+ * start angle, and the end angle are the same sign, the two control points >+ * are guaranteed to be on the same side of the line. >+ * >+ * @param rel >+ * the relationship to base on. >+ * @return the end angle or <code>Double.NaN</code> for defaults. >+ */ >+ double getEndAngle(Object rel); >+ >+ /** >+ * Gets the distance between the start point and the start control point, as >+ * a fraction of the distance between the start point and end point. >+ * >+ * @param rel >+ * the relationship to base on. >+ * @return the start distance or <code>Double.NaN</code> for defaults. >+ */ >+ double getStartDistance(Object rel); >+ >+ /** >+ * Gets the distance between the end point and the end control point, as a >+ * fraction of the distance between the start point and end point. >+ * >+ * @param rel >+ * the relationship to base on. >+ * @return the end distance or <code>Double.NaN</code> for defaults. >+ */ >+ double getEndDistance(Object rel); >+} >Index: src/org/eclipse/zest/core/viewers/IConnectionStyleProvider.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/viewers/IConnectionStyleProvider.java,v >retrieving revision 1.5 >diff -u -r1.5 IConnectionStyleProvider.java >--- src/org/eclipse/zest/core/viewers/IConnectionStyleProvider.java 12 Sep 2007 20:44:39 -0000 1.5 >+++ src/org/eclipse/zest/core/viewers/IConnectionStyleProvider.java 15 Mar 2010 03:48:53 -0000 >@@ -1,70 +1,79 @@ >-/******************************************************************************* >- * Copyright 2005-2006, 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; >- >-import org.eclipse.draw2d.IFigure; >-import org.eclipse.swt.graphics.Color; >-import org.eclipse.ui.services.IDisposable; >- >-/** >- * An extension to label providers, to supply styles for connections based upon >- * relationships, rather than on connected nodes. >- * @author Del Myers >- * @see #IGraphContentProvider >- * @see #IEntityStyleProvider >- * >- */ >-//@tag bug(151327-Styles) : created to solve this bug >-public interface IConnectionStyleProvider extends IDisposable { >- /** >- * Returns the style flags for this connection. Valid flags are those >- * that begin with CONNECTION in @see org.eclipse.zest.core.ZestStyles. Check >- * ZestStyles for legal combinations. >- * @param rel the relationship represented by this connection. >- * @return the style flags for this connection. >- * @see org.eclipse.zest.core.widgets.ZestStyles >- */ >- public int getConnectionStyle(Object rel); >- >- /** >- * Returns the color for the connection. Null for default. Any resources created by this class must be disposed by >- * this class. >- * @param rel the relationship represented by this connection. >- * @return the color. >- * @see #dispose() >- */ >- public Color getColor(Object rel); >- >- /** >- * Returns the highlighted color for this connection. Null for default. Any resources created by this class must be disposed by >- * this class. >- * @param rel the relationship represented by this connection. >- * @return the highlighted color. Null for default. >- * @see #dispose() >- */ >- public Color getHighlightColor(Object rel); >- >- /** >- * Returns the line width of the connection. -1 for default. >- * @param rel the relationship represented by this connection. >- * @return the line width for the connection. -1 for default. >- */ >- public int getLineWidth(Object rel); >- >- /** >- * Returns the tooltop for this node. If null is returned Zest will simply >- * use the default tooltip. >- * >- * @param entity >- * @return >- */ >- public IFigure getTooltip(Object entity); >-} >+/******************************************************************************* >+ * Copyright 2005-2010, 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; >+ >+import org.eclipse.draw2d.IFigure; >+import org.eclipse.swt.graphics.Color; >+import org.eclipse.ui.services.IDisposable; >+ >+/** >+ * An extension to label providers, to supply styles for connections based upon >+ * relationships, rather than on connected nodes. >+ * >+ * @author Del Myers >+ * @see #IGraphContentProvider >+ * @see #IEntityStyleProvider >+ * >+ */ >+// @tag bug(151327-Styles) : created to solve this bug >+public interface IConnectionStyleProvider extends IDisposable { >+ /** >+ * Returns the style flags for this connection. Valid flags are those that >+ * begin with CONNECTION in @see org.eclipse.zest.core.ZestStyles. Check >+ * ZestStyles for legal combinations. >+ * >+ * @param rel >+ * the relationship represented by this connection. >+ * @return the style flags for this connection. >+ * @see org.eclipse.zest.core.widgets.ZestStyles >+ */ >+ public int getConnectionStyle(Object rel); >+ >+ /** >+ * Returns the color for the connection. Null for default. Any resources >+ * created by this class must be disposed by this class. >+ * >+ * @param rel >+ * the relationship represented by this connection. >+ * @return the color. >+ * @see #dispose() >+ */ >+ public Color getColor(Object rel); >+ >+ /** >+ * Returns the highlighted color for this connection. Null for default. Any >+ * resources created by this class must be disposed by this class. >+ * >+ * @param rel >+ * the relationship represented by this connection. >+ * @return the highlighted color. Null for default. >+ * @see #dispose() >+ */ >+ public Color getHighlightColor(Object rel); >+ >+ /** >+ * Returns the line width of the connection. -1 for default. >+ * >+ * @param rel >+ * the relationship represented by this connection. >+ * @return the line width for the connection. -1 for default. >+ */ >+ public int getLineWidth(Object rel); >+ >+ /** >+ * Returns the tooltop for this node. If null is returned Zest will simply >+ * use the default tooltip. >+ * >+ * @param entity >+ * @return >+ */ >+ public IFigure getTooltip(Object entity); >+} >Index: src/org/eclipse/zest/core/viewers/IEntityConnectionStyleBezierExtension.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/viewers/IEntityConnectionStyleBezierExtension.java,v >retrieving revision 1.5 >diff -u -r1.5 IEntityConnectionStyleBezierExtension.java >--- src/org/eclipse/zest/core/viewers/IEntityConnectionStyleBezierExtension.java 12 Sep 2007 20:44:39 -0000 1.5 >+++ src/org/eclipse/zest/core/viewers/IEntityConnectionStyleBezierExtension.java 15 Mar 2010 03:48:53 -0000 >@@ -1,84 +1,84 @@ >-/******************************************************************************* >- * Copyright 2005-2006, 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; >- >-/** >- * An extension to the IEntityConnectinStyleProvider that allows styling >- * specific to bezier curves. >- * >- * Bezier curves are defined by a set of four points: two point in the layout >- * (start and end), and two related control points (also start and end). The >- * control points are defined relative to their corresponding layout point. This >- * definition includes an angle between the layout point and the line between >- * the two layout points, as well as a ratio distance from the corresponding >- * layout point. The ratio distance is defined as a fraction between 0 and 1 of >- * the distance between the two layout points. Using this definition allows >- * bezier curves to have a consistant look regardless of the actual positions of >- * the nodes in the layouts. >- * >- * @author Del Myers >- * >- */ >-// @tag zest(bug(152530-Bezier(fix))) : users can style bezier curves. >-interface IEntityConnectionStyleBezierExtension { >- >- /** >- * Gets the angle between the start point, and the line between the start >- * and end, which will define the position of the start control point. If >- * the start angle, and the end angle are the same sign, the two control >- * points are guaranteed to be on the same side of the line. >- * >- * @param source >- * the source node to base on. >- * @param dest >- * the destination node to base on. >- * @return the start angle or <code>Double.NaN</code> for defaults. >- */ >- double getStartAngle(Object source, Object dest); >- >- /** >- * Gets the angle between the end point, and the line between the start and >- * end, which will define the position of the end control point. If the >- * start angle, and the end angle are the same sign, the two control points >- * are guaranteed to be on the same side of the line. >- * >- * @param source >- * the source node to base on. >- * @param dest >- * the destination node to base on. >- * @return the end angle or <code>Double.NaN</code> for defaults. >- */ >- double getEndAngle(Object source, Object dest); >- >- /** >- * Gets the distance between the start point and the start control point, as >- * a fraction of the distance between the start point and end point. >- * >- * @param source >- * the source node to base on. >- * @param dest >- * the destination node to base on. >- * @return the start distance or <code>Double.NaN</code> for defaults. >- */ >- double getStartDistance(Object source, Object dest); >- >- /** >- * Gets the distance between the end point and the end control point, as a >- * fraction of the distance between the start point and end point. >- * >- * @param source >- * the source node to base on. >- * @param dest >- * the destination node to base on. >- * @return the end distance or <code>Double.NaN</code> for defaults. >- */ >- double getEndDistance(Object source, Object dest); >-} >+/******************************************************************************* >+ * Copyright 2005-2010, 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; >+ >+/** >+ * An extension to the IEntityConnectinStyleProvider that allows styling >+ * specific to bezier curves. >+ * >+ * Bezier curves are defined by a set of four points: two point in the layout >+ * (start and end), and two related control points (also start and end). The >+ * control points are defined relative to their corresponding layout point. This >+ * definition includes an angle between the layout point and the line between >+ * the two layout points, as well as a ratio distance from the corresponding >+ * layout point. The ratio distance is defined as a fraction between 0 and 1 of >+ * the distance between the two layout points. Using this definition allows >+ * bezier curves to have a consistant look regardless of the actual positions of >+ * the nodes in the layouts. >+ * >+ * @author Del Myers >+ * >+ */ >+// @tag zest(bug(152530-Bezier(fix))) : users can style bezier curves. >+interface IEntityConnectionStyleBezierExtension { >+ >+ /** >+ * Gets the angle between the start point, and the line between the start >+ * and end, which will define the position of the start control point. If >+ * the start angle, and the end angle are the same sign, the two control >+ * points are guaranteed to be on the same side of the line. >+ * >+ * @param source >+ * the source node to base on. >+ * @param dest >+ * the destination node to base on. >+ * @return the start angle or <code>Double.NaN</code> for defaults. >+ */ >+ double getStartAngle(Object source, Object dest); >+ >+ /** >+ * Gets the angle between the end point, and the line between the start and >+ * end, which will define the position of the end control point. If the >+ * start angle, and the end angle are the same sign, the two control points >+ * are guaranteed to be on the same side of the line. >+ * >+ * @param source >+ * the source node to base on. >+ * @param dest >+ * the destination node to base on. >+ * @return the end angle or <code>Double.NaN</code> for defaults. >+ */ >+ double getEndAngle(Object source, Object dest); >+ >+ /** >+ * Gets the distance between the start point and the start control point, as >+ * a fraction of the distance between the start point and end point. >+ * >+ * @param source >+ * the source node to base on. >+ * @param dest >+ * the destination node to base on. >+ * @return the start distance or <code>Double.NaN</code> for defaults. >+ */ >+ double getStartDistance(Object source, Object dest); >+ >+ /** >+ * Gets the distance between the end point and the end control point, as a >+ * fraction of the distance between the start point and end point. >+ * >+ * @param source >+ * the source node to base on. >+ * @param dest >+ * the destination node to base on. >+ * @return the end distance or <code>Double.NaN</code> for defaults. >+ */ >+ double getEndDistance(Object source, Object dest); >+} >Index: src/org/eclipse/zest/core/viewers/IEntityConnectionStyleProvider.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/viewers/IEntityConnectionStyleProvider.java,v >retrieving revision 1.5 >diff -u -r1.5 IEntityConnectionStyleProvider.java >--- src/org/eclipse/zest/core/viewers/IEntityConnectionStyleProvider.java 12 Sep 2007 20:44:39 -0000 1.5 >+++ src/org/eclipse/zest/core/viewers/IEntityConnectionStyleProvider.java 15 Mar 2010 03:48:53 -0000 >@@ -1,73 +1,86 @@ >-/******************************************************************************* >- * Copyright 2005-2006, 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; >- >-import org.eclipse.draw2d.IFigure; >-import org.eclipse.swt.graphics.Color; >-import org.eclipse.ui.services.IDisposable; >- >-/** >- * An extension for label providers which allows users to set styles for connections >- * that are based on entity end points. >- * @author Del Myers >- * >- */ >-//@tag bug(151327-Styles) : fix >-public interface IEntityConnectionStyleProvider extends IDisposable { >- >- /** >- * Returns the style flags for this connection. Valid flags are those >- * that begin with CONNECTION in @see org.eclipse.zest.core.ZestStyles. Check >- * ZestStyles for legal combinations. >- * @param src the source entity. >- * @param dest the destination entity. >- * @return the style flags for this connection. >- * @see org.eclipse.zest.core.widgets.ZestStyles >- */ >- public int getConnectionStyle(Object src, Object dest); >- >- /** >- * Returns the color for the connection. Null for default. >- * @param src the source entity. Any resources created by this class must be disposed by >- * this class. >- * @param dest the destination entity. >- * @return the color. >- * @see #dispose() >- */ >- public Color getColor(Object src, Object dest); >- >- /** >- * Returns the highlighted color for this connection. Null for default. >- * @param src the source entity. Any resources created by this class must be disposed by >- * this class. >- * @param dest the destination entity. >- * @return the highlighted color. Null for default. >- * @see #dispose() >- */ >- public Color getHighlightColor(Object src, Object dest); >- >- /** >- * Returns the line width of the connection. -1 for default. >- * @param src the source entity. >- * @param dest the destination entity. >- * @return the line width for the connection. -1 for default. >- */ >- public int getLineWidth(Object src, Object dest); >- >- /** >- * Returns the tooltop for this node. If null is returned Zest will simply >- * use the default tooltip. >- * >- * @param entity >- * @return >- */ >- public IFigure getTooltip(Object entity); >-} >+/******************************************************************************* >+ * Copyright 2005-2010, 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; >+ >+import org.eclipse.draw2d.IFigure; >+import org.eclipse.swt.graphics.Color; >+import org.eclipse.ui.services.IDisposable; >+ >+/** >+ * An extension for label providers which allows users to set styles for >+ * connections that are based on entity end points. >+ * >+ * @author Del Myers >+ * >+ */ >+// @tag bug(151327-Styles) : fix >+public interface IEntityConnectionStyleProvider extends IDisposable { >+ >+ /** >+ * Returns the style flags for this connection. Valid flags are those that >+ * begin with CONNECTION in @see org.eclipse.zest.core.ZestStyles. Check >+ * ZestStyles for legal combinations. >+ * >+ * @param src >+ * the source entity. >+ * @param dest >+ * the destination entity. >+ * @return the style flags for this connection. >+ * @see org.eclipse.zest.core.widgets.ZestStyles >+ */ >+ public int getConnectionStyle(Object src, Object dest); >+ >+ /** >+ * Returns the color for the connection. Null for default. >+ * >+ * @param src >+ * the source entity. Any resources created by this class must be >+ * disposed by this class. >+ * @param dest >+ * the destination entity. >+ * @return the color. >+ * @see #dispose() >+ */ >+ public Color getColor(Object src, Object dest); >+ >+ /** >+ * Returns the highlighted color for this connection. Null for default. >+ * >+ * @param src >+ * the source entity. Any resources created by this class must be >+ * disposed by this class. >+ * @param dest >+ * the destination entity. >+ * @return the highlighted color. Null for default. >+ * @see #dispose() >+ */ >+ public Color getHighlightColor(Object src, Object dest); >+ >+ /** >+ * Returns the line width of the connection. -1 for default. >+ * >+ * @param src >+ * the source entity. >+ * @param dest >+ * the destination entity. >+ * @return the line width for the connection. -1 for default. >+ */ >+ public int getLineWidth(Object src, Object dest); >+ >+ /** >+ * Returns the tooltop for this node. If null is returned Zest will simply >+ * use the default tooltip. >+ * >+ * @param entity >+ * @return >+ */ >+ public IFigure getTooltip(Object entity); >+} >Index: src/org/eclipse/zest/core/viewers/IEntityStyleProvider.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/viewers/IEntityStyleProvider.java,v >retrieving revision 1.9 >diff -u -r1.9 IEntityStyleProvider.java >--- src/org/eclipse/zest/core/viewers/IEntityStyleProvider.java 12 Sep 2007 20:44:39 -0000 1.9 >+++ src/org/eclipse/zest/core/viewers/IEntityStyleProvider.java 15 Mar 2010 03:48:54 -0000 >@@ -1,131 +1,131 @@ >-/******************************************************************************* >- * Copyright 2005-2006, 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; >- >-import org.eclipse.draw2d.IFigure; >-import org.eclipse.swt.graphics.Color; >-import org.eclipse.ui.services.IDisposable; >- >-/** >- * An extension to Label providers for graphs. Gets specific details about the >- * style of an entity before it is created. This style provider offers: >- * >- * -Background and forground colours -Hilighted and unhighlighted colours >- * (colours defined by selections). -Border color. -Highlighted and >- * unhighlighted colours for borders. -Border width -Font for text inside the >- * entity. >- * >- * Any method may return null if the Zest defaults are preferred. >- * >- * NOTE: It is up to the implementors of this interface to dispose of any Colors >- * or Fonts that are created by this class. The dispose() method will be called >- * at the end of the entity's life-cycle so that this class may dispose of its >- * resources. >- * >- * @author Del Myers >- * @see org.eclipse.jface.viewers.IColorProvider >- * @tag bug(151327-Styles) : created to solve this bug >- */ >-public interface IEntityStyleProvider extends IDisposable { >- >- /** >- * Returns the forground colour of this entity. May return null for >- * defaults. Any resources created by this class must be disposed by this >- * class. >- * >- * @param entity >- * the entity to be styled. >- * @return the forground colour of this entity. >- * @see #dispose() >- */ >- public Color getNodeHighlightColor(Object entity); >- >- /** >- * Returns the background colour for this entity. May return null for >- * defaults. Any resources created by this class must be disposed by this >- * class. >- * >- * @param entity >- * the entity to be styled. >- * @return the background colour for this entity. >- * @see #dispose() >- */ >- public Color getBorderColor(Object entity); >- >- /** >- * Returns the border highlight colour for this entity. May return null for >- * defaults. Any resources created by this class must be disposed by this >- * class. >- * >- * @param entity >- * the entity to be styled. >- * @return the border highlight colour for this entity. >- * @see #dispose() >- */ >- public Color getBorderHighlightColor(Object entity); >- >- /** >- * Returns the border width for this entity. May return -1 for defaults. >- * >- * @param entity >- * the entity to be styled. >- * @return the border width, or -1 for defaults. >- */ >- public int getBorderWidth(Object entity); >- >- /** >- * Returns true iff the adjacent entities should be highlighted when this >- * node is selected. Zest's default action is true. >- * >- * @return true iff the adjacent entities should be highlighted when this >- * node is selected. >- */ >- // @tag ADJACENT : Removed highlight adjacent >- //public boolean highlightAdjacentEntities(Object entity); >- /** >- * Returns the color that adjacent entities will be drawn when this entity >- * is selected. Will be ignored if HighlightAdjacentEntities() returns >- * false. May return null for defaults. Any resources created by this class >- * must be disposed by this class. >- * >- * @param entity >- * the entity to be styled. >- * @return the color for adjacent entities. >- * @see #highlightAdjacentEntities(Object entity) >- * @see #dispose() >- */ >- // @tag ADJACENT : Removed highlight adjacent >- //public Color getAdjacentEntityHighlightColor(Object entity); >- /** >- * Returns the colour that this node should be coloured. This will be >- * ignored if getNodeColour returns null. Any resources created by this >- * class must be diposed by this class. >- * >- * @param entity >- * The entity to be styled >- * @return The colour for the node >- * @see #dispose() >- */ >- public Color getBackgroundColour(Object entity); >- >- public Color getForegroundColour(Object entity); >- >- /** >- * Returns the tooltop for this node. If null is returned Zest will simply >- * use the default tooltip. >- * >- * @param entity >- * @return >- */ >- public IFigure getTooltip(Object entity); >- >- public boolean fisheyeNode(Object entity); >- >-} >+/******************************************************************************* >+ * Copyright 2005-2010, 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; >+ >+import org.eclipse.draw2d.IFigure; >+import org.eclipse.swt.graphics.Color; >+import org.eclipse.ui.services.IDisposable; >+ >+/** >+ * An extension to Label providers for graphs. Gets specific details about the >+ * style of an entity before it is created. This style provider offers: >+ * >+ * -Background and forground colours -Hilighted and unhighlighted colours >+ * (colours defined by selections). -Border color. -Highlighted and >+ * unhighlighted colours for borders. -Border width -Font for text inside the >+ * entity. >+ * >+ * Any method may return null if the Zest defaults are preferred. >+ * >+ * NOTE: It is up to the implementors of this interface to dispose of any Colors >+ * or Fonts that are created by this class. The dispose() method will be called >+ * at the end of the entity's life-cycle so that this class may dispose of its >+ * resources. >+ * >+ * @author Del Myers >+ * @see org.eclipse.jface.viewers.IColorProvider >+ * @tag bug(151327-Styles) : created to solve this bug >+ */ >+public interface IEntityStyleProvider extends IDisposable { >+ >+ /** >+ * Returns the forground colour of this entity. May return null for >+ * defaults. Any resources created by this class must be disposed by this >+ * class. >+ * >+ * @param entity >+ * the entity to be styled. >+ * @return the forground colour of this entity. >+ * @see #dispose() >+ */ >+ public Color getNodeHighlightColor(Object entity); >+ >+ /** >+ * Returns the background colour for this entity. May return null for >+ * defaults. Any resources created by this class must be disposed by this >+ * class. >+ * >+ * @param entity >+ * the entity to be styled. >+ * @return the background colour for this entity. >+ * @see #dispose() >+ */ >+ public Color getBorderColor(Object entity); >+ >+ /** >+ * Returns the border highlight colour for this entity. May return null for >+ * defaults. Any resources created by this class must be disposed by this >+ * class. >+ * >+ * @param entity >+ * the entity to be styled. >+ * @return the border highlight colour for this entity. >+ * @see #dispose() >+ */ >+ public Color getBorderHighlightColor(Object entity); >+ >+ /** >+ * Returns the border width for this entity. May return -1 for defaults. >+ * >+ * @param entity >+ * the entity to be styled. >+ * @return the border width, or -1 for defaults. >+ */ >+ public int getBorderWidth(Object entity); >+ >+ /** >+ * Returns true iff the adjacent entities should be highlighted when this >+ * node is selected. Zest's default action is true. >+ * >+ * @return true iff the adjacent entities should be highlighted when this >+ * node is selected. >+ */ >+ // @tag ADJACENT : Removed highlight adjacent >+ // public boolean highlightAdjacentEntities(Object entity); >+ /** >+ * Returns the color that adjacent entities will be drawn when this entity >+ * is selected. Will be ignored if HighlightAdjacentEntities() returns >+ * false. May return null for defaults. Any resources created by this class >+ * must be disposed by this class. >+ * >+ * @param entity >+ * the entity to be styled. >+ * @return the color for adjacent entities. >+ * @see #highlightAdjacentEntities(Object entity) >+ * @see #dispose() >+ */ >+ // @tag ADJACENT : Removed highlight adjacent >+ // public Color getAdjacentEntityHighlightColor(Object entity); >+ /** >+ * Returns the colour that this node should be coloured. This will be >+ * ignored if getNodeColour returns null. Any resources created by this >+ * class must be diposed by this class. >+ * >+ * @param entity >+ * The entity to be styled >+ * @return The colour for the node >+ * @see #dispose() >+ */ >+ public Color getBackgroundColour(Object entity); >+ >+ public Color getForegroundColour(Object entity); >+ >+ /** >+ * Returns the tooltop for this node. If null is returned Zest will simply >+ * use the default tooltip. >+ * >+ * @param entity >+ * @return >+ */ >+ public IFigure getTooltip(Object entity); >+ >+ public boolean fisheyeNode(Object entity); >+ >+} >Index: src/org/eclipse/zest/core/viewers/IFigureProvider.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/viewers/IFigureProvider.java,v >retrieving revision 1.1 >diff -u -r1.1 IFigureProvider.java >--- src/org/eclipse/zest/core/viewers/IFigureProvider.java 31 Mar 2009 16:39:23 -0000 1.1 >+++ src/org/eclipse/zest/core/viewers/IFigureProvider.java 15 Mar 2010 03:48:54 -0000 >@@ -1,16 +1,25 @@ >-package org.eclipse.zest.core.viewers; >- >-import org.eclipse.draw2d.IFigure; >- >-/** >- * Allows a user to create a figure for an element in >- * graph model. To use this interface, it should >- * be implemented and passed to {@link GraphViewer#setLabelProvider()} >- */ >-public interface IFigureProvider { >- >- /** >- * Creates a custom figure for a graph model element >- */ >- public IFigure getFigure(Object element); >-} >+/******************************************************************************* >+ * Copyright (c) 2009-2010 EclipseSource and others. 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: >+ * EclipseSource - initial API and implementation >+ ******************************************************************************/ >+package org.eclipse.zest.core.viewers; >+ >+import org.eclipse.draw2d.IFigure; >+ >+/** >+ * Allows a user to create a figure for an element in graph model. To use this >+ * interface, it should be implemented and passed to >+ * {@link GraphViewer#setLabelProvider()} >+ */ >+public interface IFigureProvider { >+ >+ /** >+ * Creates a custom figure for a graph model element >+ */ >+ public IFigure getFigure(Object element); >+} >Index: src/org/eclipse/zest/core/viewers/IGraphContentProvider.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/viewers/IGraphContentProvider.java,v >retrieving revision 1.6 >diff -u -r1.6 IGraphContentProvider.java >--- src/org/eclipse/zest/core/viewers/IGraphContentProvider.java 12 Sep 2007 20:44:39 -0000 1.6 >+++ src/org/eclipse/zest/core/viewers/IGraphContentProvider.java 15 Mar 2010 03:48:54 -0000 >@@ -1,51 +1,58 @@ >-/******************************************************************************* >- * Copyright 2005, 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; >- >-import org.eclipse.jface.viewers.IStructuredContentProvider; >- >-/** >- * A graph content provider. >- * >- * @author Ian Bull >- */ >-public interface IGraphContentProvider extends IStructuredContentProvider { >- >- /** >- * Gets the source Object for the given relationship. Note, at least one of the source >- * or destination must not be null. If both are null, then nothing can be displayed in >- * the graph (a relationship cannot exist without nodes to be connected to). However, >- * if one of getSource() or getDestination() returns null, then the resulting graph will >- * contain an unconnected node for the non-null object returned from the other method. >- * @param rel the relationship. >- * @return the source, or null for an unconnected destination. >- */ >- public Object getSource(Object rel); >- >- /** >- * Gets the target Object for the given relationship. Note, at least one of the source >- * or destination must not be null. If both are null, then nothing can be displayed in >- * the graph (a relationship cannot exist without nodes to be connected to). However, >- * if one of getSource() or getDestination() returns null, then the resulting graph will >- * contain an unconnected node for the non-null object returned from the other method. >- * @param rel the relationship. >- * @return the destination, or null for an unconnected source. >- */ >- public Object getDestination(Object rel); >- >- /** >- * Returns all the relationships in the graph for the given input. >- * @input the input model object. >- * @return all the relationships in the graph for the given input. >- */ >- public Object[] getElements(Object input); >- >-} >+/******************************************************************************* >+ * Copyright 2005-2010, 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; >+ >+import org.eclipse.jface.viewers.IStructuredContentProvider; >+ >+/** >+ * A graph content provider. >+ * >+ * @author Ian Bull >+ */ >+public interface IGraphContentProvider extends IStructuredContentProvider { >+ >+ /** >+ * Gets the source Object for the given relationship. Note, at least one of >+ * the source or destination must not be null. If both are null, then >+ * nothing can be displayed in the graph (a relationship cannot exist >+ * without nodes to be connected to). However, if one of getSource() or >+ * getDestination() returns null, then the resulting graph will contain an >+ * unconnected node for the non-null object returned from the other method. >+ * >+ * @param rel >+ * the relationship. >+ * @return the source, or null for an unconnected destination. >+ */ >+ public Object getSource(Object rel); >+ >+ /** >+ * Gets the target Object for the given relationship. Note, at least one of >+ * the source or destination must not be null. If both are null, then >+ * nothing can be displayed in the graph (a relationship cannot exist >+ * without nodes to be connected to). However, if one of getSource() or >+ * getDestination() returns null, then the resulting graph will contain an >+ * unconnected node for the non-null object returned from the other method. >+ * >+ * @param rel >+ * the relationship. >+ * @return the destination, or null for an unconnected source. >+ */ >+ public Object getDestination(Object rel); >+ >+ /** >+ * Returns all the relationships in the graph for the given input. >+ * >+ * @input the input model object. >+ * @return all the relationships in the graph for the given input. >+ */ >+ public Object[] getElements(Object input); >+ >+} >Index: src/org/eclipse/zest/core/viewers/IGraphEntityContentProvider.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/viewers/IGraphEntityContentProvider.java,v >retrieving revision 1.5 >diff -u -r1.5 IGraphEntityContentProvider.java >--- src/org/eclipse/zest/core/viewers/IGraphEntityContentProvider.java 12 Sep 2007 20:44:39 -0000 1.5 >+++ src/org/eclipse/zest/core/viewers/IGraphEntityContentProvider.java 15 Mar 2010 03:48:54 -0000 >@@ -1,31 +1,32 @@ >-/******************************************************************************* >- * Copyright 2005, 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; >- >-import org.eclipse.jface.viewers.IStructuredContentProvider; >- >-/** >- * >- * @author Ian Bull >- * >- */ >-public interface IGraphEntityContentProvider extends IStructuredContentProvider { >- >- public Object[] getElements(Object inputElement); >- >- /** >- * Gets the elements this object is connected to >- * @param entity >- * @return >- */ >- public Object[] getConnectedTo(Object entity); >- >-} >+/******************************************************************************* >+ * Copyright 2005-2010, 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; >+ >+import org.eclipse.jface.viewers.IStructuredContentProvider; >+ >+/** >+ * >+ * @author Ian Bull >+ * >+ */ >+public interface IGraphEntityContentProvider extends IStructuredContentProvider { >+ >+ public Object[] getElements(Object inputElement); >+ >+ /** >+ * Gets the elements this object is connected to >+ * >+ * @param entity >+ * @return >+ */ >+ public Object[] getConnectedTo(Object entity); >+ >+} >Index: src/org/eclipse/zest/core/viewers/IGraphEntityRelationshipContentProvider.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/viewers/IGraphEntityRelationshipContentProvider.java,v >retrieving revision 1.4 >diff -u -r1.4 IGraphEntityRelationshipContentProvider.java >--- src/org/eclipse/zest/core/viewers/IGraphEntityRelationshipContentProvider.java 12 Sep 2007 20:44:39 -0000 1.4 >+++ src/org/eclipse/zest/core/viewers/IGraphEntityRelationshipContentProvider.java 15 Mar 2010 03:48:54 -0000 >@@ -1,32 +1,39 @@ >-/******************************************************************************* >- * Copyright 2005-2006, 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; >- >-import org.eclipse.jface.viewers.IStructuredContentProvider; >- >-/** >- * A content provider that is node-relationship centric. Call-backs return model >- * nodes to the user, and ask for relationships. Both nodes and relationships are >- * represented by the user's model. >- * @author Del Myers >- * >- */ >-//@tag bug.154580-Content.fix : new content provider that returns relationships for the given source and destination. >-public interface IGraphEntityRelationshipContentProvider extends IStructuredContentProvider { >- /** >- * Gets the relationships between the given source and destination nodes. >- * @param source the source node. >- * @param dest the destination node. >- * @return objects represtenting the different relationships between the nodes. >- */ >- public Object[] getRelationships(Object source, Object dest); >- >-} >+/******************************************************************************* >+ * Copyright 2005-2010, 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; >+ >+import org.eclipse.jface.viewers.IStructuredContentProvider; >+ >+/** >+ * A content provider that is node-relationship centric. Call-backs return model >+ * nodes to the user, and ask for relationships. Both nodes and relationships >+ * are represented by the user's model. >+ * >+ * @author Del Myers >+ * >+ */ >+// @tag bug.154580-Content.fix : new content provider that returns relationships >+// for the given source and destination. >+public interface IGraphEntityRelationshipContentProvider extends >+ IStructuredContentProvider { >+ /** >+ * Gets the relationships between the given source and destination nodes. >+ * >+ * @param source >+ * the source node. >+ * @param dest >+ * the destination node. >+ * @return objects represtenting the different relationships between the >+ * nodes. >+ */ >+ public Object[] getRelationships(Object source, Object dest); >+ >+} >Index: src/org/eclipse/zest/core/viewers/INestedContentProvider.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/viewers/INestedContentProvider.java,v >retrieving revision 1.1 >diff -u -r1.1 INestedContentProvider.java >--- src/org/eclipse/zest/core/viewers/INestedContentProvider.java 20 Nov 2008 00:09:54 -0000 1.1 >+++ src/org/eclipse/zest/core/viewers/INestedContentProvider.java 15 Mar 2010 03:48:54 -0000 >@@ -1,41 +1,43 @@ >-/******************************************************************************* >- * Copyright (c) 2000, 2008 IBM Corporation and others. 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: IBM Corporation - initial API and implementation Chisel Group, >- * University of Victoria >- ******************************************************************************/ >-package org.eclipse.zest.core.viewers; >- >-/* >- * A content provider for nested graphs. Any entity based content provider >- * (IGraphEntityContentProvider or IGraphEntityRelationshipContentProvider) can >- * also implement this interface. Any node that "hasChildren" will be rendered >- * as a container. >- * >- * Note: Containers cannot contain other containers. >- * >- * @author irbull >- */ >-public interface INestedContentProvider { >- >- /** >- * Does the current node have children? If so, it will be rendered as a >- * container. >- * >- * @param element The current node >- * @return True if it has children, false otherwise >- */ >- public boolean hasChildren(Object element); >- >- /** >- * Gets the children of this node. This method will not be called >- * if hasChildren returns false. >- * >- * @param element The current node >- * @return The list of children for this node. >- */ >- public Object[] getChildren(Object element); >-} >+/******************************************************************************* >+ * Copyright (c) 2009-2010 EclipseSource and others. 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: >+ * EclipseSource - initial API and implementation >+ ******************************************************************************/ >+package org.eclipse.zest.core.viewers; >+ >+/** >+ * A content provider for nested graphs. Any entity based content provider >+ * (IGraphEntityContentProvider or IGraphEntityRelationshipContentProvider) can >+ * also implement this interface. Any node that "hasChildren" will be rendered >+ * as a container. >+ * >+ * Note: Containers cannot contain other containers. >+ * >+ * @author irbull >+ */ >+public interface INestedContentProvider { >+ >+ /** >+ * Does the current node have children? If so, it will be rendered as a >+ * container. >+ * >+ * @param element >+ * The current node >+ * @return True if it has children, false otherwise >+ */ >+ public boolean hasChildren(Object element); >+ >+ /** >+ * Gets the children of this node. This method will not be called if >+ * hasChildren returns false. >+ * >+ * @param element >+ * The current node >+ * @return The list of children for this node. >+ */ >+ public Object[] getChildren(Object element); >+} >Index: src/org/eclipse/zest/core/viewers/ISelfStyleProvider.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/viewers/ISelfStyleProvider.java,v >retrieving revision 1.1 >diff -u -r1.1 ISelfStyleProvider.java >--- src/org/eclipse/zest/core/viewers/ISelfStyleProvider.java 30 Jan 2009 01:14:33 -0000 1.1 >+++ src/org/eclipse/zest/core/viewers/ISelfStyleProvider.java 15 Mar 2010 03:48:54 -0000 >@@ -1,32 +1,32 @@ >-/******************************************************************************* >- * Copyright (c) 2009 EclipseSource and others. 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: EclipseSource - initial API and implementation >- ******************************************************************************/ >-package org.eclipse.zest.core.viewers; >- >-import org.eclipse.zest.core.widgets.GraphConnection; >-import org.eclipse.zest.core.widgets.GraphNode; >- >-/** >- * Provides a mechanism to style nodes and edges when they are created. >- * >- * After each node or edge is created, the self styling method will be called with both >- * the element and the widget. >- */ >-public interface ISelfStyleProvider { >- >- /** >- * Styles a connection >- */ >- public void selfStyleConnection(Object element, GraphConnection connection); >- >- /** >- * Styles a node >- */ >- public void selfStyleNode(Object element, GraphNode node); >- >-} >+/******************************************************************************* >+ * Copyright (c) 2009-2010 EclipseSource and others. 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: EclipseSource - initial API and implementation >+ ******************************************************************************/ >+package org.eclipse.zest.core.viewers; >+ >+import org.eclipse.zest.core.widgets.GraphConnection; >+import org.eclipse.zest.core.widgets.GraphNode; >+ >+/** >+ * Provides a mechanism to style nodes and edges when they are created. >+ * >+ * After each node or edge is created, the self styling method will be called >+ * with both the element and the widget. >+ */ >+public interface ISelfStyleProvider { >+ >+ /** >+ * Styles a connection >+ */ >+ public void selfStyleConnection(Object element, GraphConnection connection); >+ >+ /** >+ * Styles a node >+ */ >+ public void selfStyleNode(Object element, GraphNode node); >+ >+} >Index: src/org/eclipse/zest/core/viewers/IZoomableWorkbenchPart.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/viewers/IZoomableWorkbenchPart.java,v >retrieving revision 1.4 >diff -u -r1.4 IZoomableWorkbenchPart.java >--- src/org/eclipse/zest/core/viewers/IZoomableWorkbenchPart.java 12 Sep 2007 20:44:39 -0000 1.4 >+++ src/org/eclipse/zest/core/viewers/IZoomableWorkbenchPart.java 15 Mar 2010 03:48:54 -0000 >@@ -1,27 +1,28 @@ >-/******************************************************************************* >- * Copyright 2005-2006, 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; >- >- >-/** >- * An interface that can be added to IWorkbenchParts based on ZEST views so that zooming >- * is supported. >- * @author Del Myers >- * >- */ >-//@tag bug.156286-Zooming.fix : experimental >-public interface IZoomableWorkbenchPart { >- /** >- * Returns the viewer that is zoomable. >- * @return the viewer that is zoomable. >- */ >- AbstractZoomableViewer getZoomableViewer(); >-} >+/******************************************************************************* >+ * Copyright 2005-2010, 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; >+ >+/** >+ * An interface that can be added to IWorkbenchParts based on ZEST views so that >+ * zooming is supported. >+ * >+ * @author Del Myers >+ * >+ */ >+// @tag bug.156286-Zooming.fix : experimental >+public interface IZoomableWorkbenchPart { >+ /** >+ * Returns the viewer that is zoomable. >+ * >+ * @return the viewer that is zoomable. >+ */ >+ AbstractZoomableViewer getZoomableViewer(); >+} >Index: src/org/eclipse/zest/core/viewers/ZoomContributionViewItem.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/viewers/ZoomContributionViewItem.java,v >retrieving revision 1.7 >diff -u -r1.7 ZoomContributionViewItem.java >--- src/org/eclipse/zest/core/viewers/ZoomContributionViewItem.java 17 Jun 2008 04:08:35 -0000 1.7 >+++ src/org/eclipse/zest/core/viewers/ZoomContributionViewItem.java 15 Mar 2010 03:48:54 -0000 >@@ -1,241 +1,247 @@ >-/******************************************************************************* >- * Copyright 2005-2006, 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; >- >-import org.eclipse.jface.action.ContributionItem; >-import org.eclipse.zest.core.viewers.internal.ZoomListener; >-import org.eclipse.zest.core.viewers.internal.ZoomManager; >-import org.eclipse.swt.SWT; >-import org.eclipse.swt.events.MenuAdapter; >-import org.eclipse.swt.events.MenuEvent; >-import org.eclipse.swt.events.SelectionAdapter; >-import org.eclipse.swt.events.SelectionEvent; >-import org.eclipse.swt.widgets.Combo; >-import org.eclipse.swt.widgets.Composite; >-import org.eclipse.swt.widgets.CoolBar; >-import org.eclipse.swt.widgets.CoolItem; >-import org.eclipse.swt.widgets.Menu; >-import org.eclipse.swt.widgets.MenuItem; >-import org.eclipse.swt.widgets.ToolBar; >-import org.eclipse.swt.widgets.ToolItem; >- >-/** >- * A contribution item that adds a combo to a toolbar or coolbar, or a list of >- * zooms to a menu. Can only be used for one toolbar, coolbar, or menu. >- * >- * In order to use this item, let your workbench part implement >- * IZoomableWorkbenchPart. If the workbench part then supplies a viewer that is >- * zoomable, the combo or menu created by this item will be enabled. >- * >- * @author Del Myers >- * >- */ >-//@tag zest.bug.156286-Zooming.fix : create a contribution item that can set zooming on Zest views. >-public class ZoomContributionViewItem extends ContributionItem implements ZoomListener { >- /** >- * Zooms to fit the width. >- */ >- public static final String FIT_WIDTH = ZoomManager.FIT_WIDTH; >- /** >- * Zooms to fit the height. >- */ >- public static final String FIT_HEIGHT = ZoomManager.FIT_HEIGHT; >- /** >- * Zooms to fit entirely within the viewport. >- */ >- public static final String FIT_ALL = ZoomManager.FIT_ALL; >- >- private String[] zoomLevels; >- private ZoomManager zoomManager; >- private Combo combo; >- private Menu fMenu; >- private MenuAdapter menuAdapter = new MenuAdapter() { >- public void menuShown(MenuEvent e) { >- refresh(true); >- } >- }; >- >- /** >- * Creates a new contribution item that will work on the given part >- * service.initialZooms will be used to populate the combo or the menu. >- * Valid values for initialZooms are percentage numbers (e.g., "100%"), or >- * FIT_WIDTH, FIT_HEIGHT, FIT_ALL. >- * >- * @param partService >- * service used to see whether the view is zoomable. >- */ >- public ZoomContributionViewItem(IZoomableWorkbenchPart part) { >- zoomManager = part.getZoomableViewer().getZoomManager(); >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.jface.action.ContributionItem#fill(org.eclipse.swt.widgets.Menu, >- * int) >- */ >- public void fill(Menu menu, int index) { >- if (this.fMenu == null || this.fMenu != menu) { >- if (this.fMenu != null) { >- this.fMenu.removeMenuListener(menuAdapter); >- this.fMenu = null; >- } >- this.fMenu = menu; >- menu.addMenuListener(menuAdapter); >- } >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.jface.action.ContributionItem#fill(org.eclipse.swt.widgets.CoolBar, >- * int) >- */ >- public void fill(CoolBar parent, int index) { >- CoolItem item = new CoolItem(parent, SWT.DROP_DOWN); >- Combo combo = createCombo(parent); >- item.setControl(combo); >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.jface.action.ContributionItem#fill(org.eclipse.swt.widgets.ToolBar, >- * int) >- */ >- public void fill(ToolBar parent, int index) { >- ToolItem item = new ToolItem(parent, SWT.DROP_DOWN); >- Combo combo = createCombo(parent); >- item.setControl(combo); >- } >- >- private Combo createCombo(Composite parent) { >- this.combo = new Combo(parent, SWT.DROP_DOWN); >- this.combo.setItems(zoomLevels); >- this.combo.addSelectionListener(new SelectionAdapter() { >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent) >- */ >- public void widgetSelected(SelectionEvent e) { >- int selection = combo.getSelectionIndex(); >- if (selection > 0) { >- doZoom(combo.getItem(selection)); >- } else { >- doZoom(combo.getItem(0)); >- } >- } >- }); >- return this.combo; >- } >- >- private void doZoom(String zoom) { >- if (zoomManager != null) { >- zoomManager.setZoomAsText(zoom); >- } >- } >- >- private void refresh(boolean rebuild) { >- // >- if (combo != null && !combo.isDisposed()) { >- refreshCombo(rebuild); >- } else if (fMenu != null && !fMenu.isDisposed()) { >- refreshMenu(rebuild); >- } >- } >- >- /** >- * @param rebuild >- */ >- private void refreshMenu(boolean rebuild) { >- fMenu.setEnabled(false); >- if (zoomManager == null) { >- return; >- } >- if (rebuild) { >- zoomLevels = zoomManager.getZoomLevelsAsText(); >- MenuItem[] oldItems = fMenu.getItems(); >- for (int i = 0; i < oldItems.length; i++) { >- if (oldItems[i].getData() == this) { >- oldItems[i].dispose(); >- } >- } >- for (int i = 0; i < zoomLevels.length; i++) { >- MenuItem item = new MenuItem(fMenu, SWT.RADIO); >- item.setText(zoomLevels[i]); >- item.setData(this); >- item.addSelectionListener(new SelectionAdapter() { >- public void widgetSelected(SelectionEvent e) { >- MenuItem source = (MenuItem) e.getSource(); >- doZoom(source.getText()); >- } >- }); >- } >- } >- String zoom = zoomManager.getZoomAsText(); >- MenuItem[] items = fMenu.getItems(); >- for (int i = 0; i < items.length; i++) { >- MenuItem item = items[i]; >- if (item.getData() == this) { >- item.setSelection(false); >- if (zoom.equalsIgnoreCase(item.getText())) { >- item.setSelection(true); >- } >- } >- } >- fMenu.setEnabled(true); >- } >- >- /** >- * @param rebuild >- */ >- private void refreshCombo(boolean rebuild) { >- combo.setEnabled(false); >- if (zoomManager == null) { >- return; >- } >- if (rebuild) { >- combo.setItems(zoomManager.getZoomLevelsAsText()); >- } >- String zoom = zoomManager.getZoomAsText(); >- int index = combo.indexOf(zoom); >- if (index > 0) { >- combo.select(index); >- } >- combo.setEnabled(true); >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.gef.editparts.ZoomListener#zoomChanged(double) >- */ >- public void zoomChanged(double z) { >- refresh(false); >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.jface.action.ContributionItem#dispose() >- */ >- >- public void dispose() { >- if (combo != null) { >- combo = null; >- } >- if (fMenu != null) { >- fMenu = null; >- } >- // @tag zest.bug.159667-ZoomDispose : make sure that we no longer listen to the part service. >- super.dispose(); >- } >-} >+/******************************************************************************* >+ * Copyright 2005-2010, 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; >+ >+import org.eclipse.jface.action.ContributionItem; >+import org.eclipse.swt.SWT; >+import org.eclipse.swt.events.MenuAdapter; >+import org.eclipse.swt.events.MenuEvent; >+import org.eclipse.swt.events.SelectionAdapter; >+import org.eclipse.swt.events.SelectionEvent; >+import org.eclipse.swt.widgets.Combo; >+import org.eclipse.swt.widgets.Composite; >+import org.eclipse.swt.widgets.CoolBar; >+import org.eclipse.swt.widgets.CoolItem; >+import org.eclipse.swt.widgets.Menu; >+import org.eclipse.swt.widgets.MenuItem; >+import org.eclipse.swt.widgets.ToolBar; >+import org.eclipse.swt.widgets.ToolItem; >+ >+/** >+ * A contribution item that adds a combo to a toolbar or coolbar, or a list of >+ * zooms to a menu. Can only be used for one toolbar, coolbar, or menu. >+ * >+ * In order to use this item, let your workbench part implement >+ * IZoomableWorkbenchPart. If the workbench part then supplies a viewer that is >+ * zoomable, the combo or menu created by this item will be enabled. >+ * >+ * @author Del Myers >+ * >+ */ >+// @tag zest.bug.156286-Zooming.fix : create a contribution item that can set >+// zooming on Zest views. >+public class ZoomContributionViewItem extends ContributionItem implements >+ ZoomListener { >+ /** >+ * Zooms to fit the width. >+ */ >+ public static final String FIT_WIDTH = ZoomManager.FIT_WIDTH; >+ /** >+ * Zooms to fit the height. >+ */ >+ public static final String FIT_HEIGHT = ZoomManager.FIT_HEIGHT; >+ /** >+ * Zooms to fit entirely within the viewport. >+ */ >+ public static final String FIT_ALL = ZoomManager.FIT_ALL; >+ >+ private String[] zoomLevels; >+ private ZoomManager zoomManager; >+ private Combo combo; >+ private Menu fMenu; >+ private MenuAdapter menuAdapter = new MenuAdapter() { >+ public void menuShown(MenuEvent e) { >+ refresh(true); >+ } >+ }; >+ >+ /** >+ * Creates a new contribution item that will work on the given part >+ * service.initialZooms will be used to populate the combo or the menu. >+ * Valid values for initialZooms are percentage numbers (e.g., "100%"), or >+ * FIT_WIDTH, FIT_HEIGHT, FIT_ALL. >+ * >+ * @param partService >+ * service used to see whether the view is zoomable. >+ */ >+ public ZoomContributionViewItem(IZoomableWorkbenchPart part) { >+ zoomManager = part.getZoomableViewer().getZoomManager(); >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see >+ * org.eclipse.jface.action.ContributionItem#fill(org.eclipse.swt.widgets >+ * .Menu, int) >+ */ >+ public void fill(Menu menu, int index) { >+ if (this.fMenu == null || this.fMenu != menu) { >+ if (this.fMenu != null) { >+ this.fMenu.removeMenuListener(menuAdapter); >+ this.fMenu = null; >+ } >+ this.fMenu = menu; >+ menu.addMenuListener(menuAdapter); >+ } >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see >+ * org.eclipse.jface.action.ContributionItem#fill(org.eclipse.swt.widgets >+ * .CoolBar, int) >+ */ >+ public void fill(CoolBar parent, int index) { >+ CoolItem item = new CoolItem(parent, SWT.DROP_DOWN); >+ Combo combo = createCombo(parent); >+ item.setControl(combo); >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see >+ * org.eclipse.jface.action.ContributionItem#fill(org.eclipse.swt.widgets >+ * .ToolBar, int) >+ */ >+ public void fill(ToolBar parent, int index) { >+ ToolItem item = new ToolItem(parent, SWT.DROP_DOWN); >+ Combo combo = createCombo(parent); >+ item.setControl(combo); >+ } >+ >+ private Combo createCombo(Composite parent) { >+ this.combo = new Combo(parent, SWT.DROP_DOWN); >+ this.combo.setItems(zoomLevels); >+ this.combo.addSelectionListener(new SelectionAdapter() { >+ /* >+ * (non-Javadoc) >+ * >+ * @see >+ * org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse >+ * .swt.events.SelectionEvent) >+ */ >+ public void widgetSelected(SelectionEvent e) { >+ int selection = combo.getSelectionIndex(); >+ if (selection > 0) { >+ doZoom(combo.getItem(selection)); >+ } else { >+ doZoom(combo.getItem(0)); >+ } >+ } >+ }); >+ return this.combo; >+ } >+ >+ private void doZoom(String zoom) { >+ if (zoomManager != null) { >+ zoomManager.setZoomAsText(zoom); >+ } >+ } >+ >+ private void refresh(boolean rebuild) { >+ // >+ if (combo != null && !combo.isDisposed()) { >+ refreshCombo(rebuild); >+ } else if (fMenu != null && !fMenu.isDisposed()) { >+ refreshMenu(rebuild); >+ } >+ } >+ >+ /** >+ * @param rebuild >+ */ >+ private void refreshMenu(boolean rebuild) { >+ fMenu.setEnabled(false); >+ if (zoomManager == null) { >+ return; >+ } >+ if (rebuild) { >+ zoomLevels = zoomManager.getZoomLevelsAsText(); >+ MenuItem[] oldItems = fMenu.getItems(); >+ for (int i = 0; i < oldItems.length; i++) { >+ if (oldItems[i].getData() == this) { >+ oldItems[i].dispose(); >+ } >+ } >+ for (int i = 0; i < zoomLevels.length; i++) { >+ MenuItem item = new MenuItem(fMenu, SWT.RADIO); >+ item.setText(zoomLevels[i]); >+ item.setData(this); >+ item.addSelectionListener(new SelectionAdapter() { >+ public void widgetSelected(SelectionEvent e) { >+ MenuItem source = (MenuItem) e.getSource(); >+ doZoom(source.getText()); >+ } >+ }); >+ } >+ } >+ String zoom = zoomManager.getZoomAsText(); >+ MenuItem[] items = fMenu.getItems(); >+ for (int i = 0; i < items.length; i++) { >+ MenuItem item = items[i]; >+ if (item.getData() == this) { >+ item.setSelection(false); >+ if (zoom.equalsIgnoreCase(item.getText())) { >+ item.setSelection(true); >+ } >+ } >+ } >+ fMenu.setEnabled(true); >+ } >+ >+ /** >+ * @param rebuild >+ */ >+ private void refreshCombo(boolean rebuild) { >+ combo.setEnabled(false); >+ if (zoomManager == null) { >+ return; >+ } >+ if (rebuild) { >+ combo.setItems(zoomManager.getZoomLevelsAsText()); >+ } >+ String zoom = zoomManager.getZoomAsText(); >+ int index = combo.indexOf(zoom); >+ if (index > 0) { >+ combo.select(index); >+ } >+ combo.setEnabled(true); >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see org.eclipse.gef.editparts.ZoomListener#zoomChanged(double) >+ */ >+ public void zoomChanged(double z) { >+ refresh(false); >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see org.eclipse.jface.action.ContributionItem#dispose() >+ */ >+ >+ public void dispose() { >+ if (combo != null) { >+ combo = null; >+ } >+ if (fMenu != null) { >+ fMenu = null; >+ } >+ // @tag zest.bug.159667-ZoomDispose : make sure that we no longer listen >+ // to the part service. >+ super.dispose(); >+ } >+} >Index: src/org/eclipse/zest/core/viewers/ZoomListener.java >=================================================================== >RCS file: src/org/eclipse/zest/core/viewers/ZoomListener.java >diff -N src/org/eclipse/zest/core/viewers/ZoomListener.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/zest/core/viewers/ZoomListener.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,27 @@ >+/******************************************************************************* >+ * Copyright (c) 2000, 2010 IBM Corporation and others. 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: IBM Corporation - initial API and implementation >+ *******************************************************************************/ >+package org.eclipse.zest.core.viewers; >+ >+/** >+ * Listens to zoom level changes. >+ * >+ * @author Eric Bordeau >+ * @since 2.0 >+ */ >+public interface ZoomListener { >+ >+ /** >+ * Called whenever the ZoomManager's zoom level changes. >+ * >+ * @param zoom >+ * the new zoom level. >+ */ >+ void zoomChanged(double zoom); >+ >+} >Index: src/org/eclipse/zest/core/viewers/ZoomManager.java >=================================================================== >RCS file: src/org/eclipse/zest/core/viewers/ZoomManager.java >diff -N src/org/eclipse/zest/core/viewers/ZoomManager.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/zest/core/viewers/ZoomManager.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,611 @@ >+/******************************************************************************* >+ * Copyright (c) 2000, 2010 IBM Corporation and others. 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: IBM Corporation - initial API and implementation >+ ******************************************************************************/ >+package org.eclipse.zest.core.viewers; >+ >+import java.util.ArrayList; >+import java.util.Collections; >+import java.util.Iterator; >+import java.util.List; >+ >+import org.eclipse.draw2d.FreeformFigure; >+import org.eclipse.draw2d.IFigure; >+import org.eclipse.draw2d.ScalableFigure; >+import org.eclipse.draw2d.ScalableFreeformLayeredPane; >+import org.eclipse.draw2d.Viewport; >+import org.eclipse.draw2d.geometry.Dimension; >+import org.eclipse.draw2d.geometry.Point; >+import org.eclipse.draw2d.geometry.Rectangle; >+import org.eclipse.swt.widgets.Display; >+import org.eclipse.zest.core.viewers.internal.SharedMessages; >+ >+/** >+ * Manage the primary zoom function in a graphical viewer. This class is used by >+ * the zoom contribution items, including: >+ * <UL> >+ * <LI>{@link org.eclipse.gef.ui.actions.ZoomInAction} >+ * <LI>{@link org.eclipse.gef.ui.actions.ZoomOutAction} >+ * <LI>and {@link org.eclipse.gef.ui.actions.ZoomComboContributionItem} >+ * </UL> >+ * <P> >+ * A ZoomManager controls how zoom in and zoom out are performed. It also >+ * determines the list of choices the user sees in the drop-down Combo on the >+ * toolbar. The zoom manager controls a <code>ScalableFigure</code>, which >+ * performs the actual zoom, and also a <code>Viewport</code>. The viewport is >+ * needed so that the scrolled location is preserved as the zoom level changes. >+ * <p> >+ * <b>NOTE:</b> For the settings of {@link #FIT_ALL Page}, {@link #FIT_WIDTH >+ * Width} and {@link #FIT_HEIGHT Height} to work properly, the given >+ * <code>Viewport</code> should have its scrollbars always visible or never >+ * visible. Otherwise, these settings may cause undesired effects. >+ * >+ * @author Dan Lee >+ * @author Eric Bordeau >+ * @since 2.0 >+ */ >+public class ZoomManager { >+ >+ /** Style bit meaning don't animate any zooms */ >+ public static final int ANIMATE_NEVER = 0; >+ /** Style bit meaning animate during {@link #zoomIn()} and {@link #zoomOut()} */ >+ public static final int ANIMATE_ZOOM_IN_OUT = 1; >+ >+ private List listeners = new ArrayList(); >+ >+ private double multiplier = 1.0; >+ private ScalableFigure pane; >+ private Viewport viewport; >+ private double zoom = 1.0; >+ // private int zoomAnimationStyle = ANIMATE_NEVER; >+ private String currentZoomContant = null; >+ private double[] zoomLevels = { .5, .75, 1.0, 1.5, 2.0, 2.5, 3, 4 }; >+ >+ /** >+ * String constant for the "Height" zoom level. At this zoom level, the zoom >+ * manager will adopt a zoom setting such that the entire height of the >+ * diagram will be visible on the screen. >+ */ >+ public static final String FIT_HEIGHT = SharedMessages.FitHeightAction_Label; >+ /** >+ * String constant for the "Width" zoom level. At this zoom level, the zoom >+ * manager will adopt a zoom setting such that the entire width of the >+ * diagram will be visible on the screen. >+ */ >+ public static final String FIT_WIDTH = SharedMessages.FitWidthAction_Label; >+ /** >+ * String constant for the "Page" zoom level. At this zoom level, the zoom >+ * manager will adopt a zoom setting such that the entire diagram will be >+ * visible on the screen. >+ */ >+ public static final String FIT_ALL = SharedMessages.FitAllAction_Label; >+ private List zoomLevelContributions = Collections.EMPTY_LIST; >+ >+ //DecimalFormat format = new DecimalFormat("####%"); //$NON-NLS-1$ >+ >+ /** >+ * Creates a new ZoomManager. >+ * >+ * @param pane >+ * The ScalableFigure associated with this ZoomManager >+ * @param viewport >+ * The Viewport assoicated with this ZoomManager >+ */ >+ public ZoomManager(ScalableFigure pane, Viewport viewport) { >+ this.pane = pane; >+ this.viewport = viewport; >+ zoomLevelContributions = new ArrayList(); >+ zoomLevelContributions.add(FIT_ALL); >+ } >+ >+ /** >+ * @deprecated Use {@link #ZoomManager(ScalableFigure, Viewport)} instead. >+ * Creates a new ZoomManager >+ * @param pane >+ * The ScalableFreeformLayeredPane associated with this >+ * ZoomManager >+ * @param viewport >+ * The Viewport assoicated with this viewport >+ */ >+ public ZoomManager(ScalableFreeformLayeredPane pane, Viewport viewport) { >+ this.pane = pane; >+ this.viewport = viewport; >+ } >+ >+ /** >+ * Adds the given ZoomListener to this ZoomManager's list of listeners. >+ * >+ * @param listener >+ * the ZoomListener to be added >+ */ >+ public void addZoomListener(ZoomListener listener) { >+ listeners.add(listener); >+ } >+ >+ /** >+ * returns <code>true</code> if the zoommanager can perform >+ * <code>zoomIn()</code>. >+ * >+ * @return boolean true if zoomIn can be called >+ */ >+ public boolean canZoomIn() { >+ return getZoom() < getMaxZoom(); >+ } >+ >+ /** >+ * returns <code>true</code> if the zoommanager can perform >+ * <code>zoomOut()</code>. >+ * >+ * @return boolean true if zoomOut can be called >+ */ >+ public boolean canZoomOut() { >+ return getZoom() > getMinZoom(); >+ } >+ >+ /** >+ * Notifies listeners that the zoom level has changed. >+ */ >+ protected void fireZoomChanged() { >+ Iterator iter = listeners.iterator(); >+ while (iter.hasNext()) { >+ ((ZoomListener) iter.next()).zoomChanged(zoom); >+ } >+ } >+ >+ private double getFitXZoomLevel(int which) { >+ IFigure fig = getScalableFigure(); >+ >+ Dimension available = getViewport().getClientArea().getSize(); >+ Dimension desired; >+ if (fig instanceof FreeformFigure) { >+ desired = ((FreeformFigure) fig).getFreeformExtent().getCopy() >+ .union(0, 0).getSize(); >+ } else { >+ desired = fig.getPreferredSize().getCopy(); >+ } >+ >+ desired.width -= fig.getInsets().getWidth(); >+ desired.height -= fig.getInsets().getHeight(); >+ >+ while (fig != getViewport()) { >+ available.width -= fig.getInsets().getWidth(); >+ available.height -= fig.getInsets().getHeight(); >+ fig = fig.getParent(); >+ } >+ >+ double scaleX = Math.min(available.width * zoom / desired.width, >+ getMaxZoom()); >+ double scaleY = Math.min(available.height * zoom / desired.height, >+ getMaxZoom()); >+ if (which == 0) { >+ return scaleX; >+ } >+ if (which == 1) { >+ return scaleY; >+ } >+ return Math.min(scaleX, scaleY); >+ } >+ >+ /** >+ * Calculates and returns the zoom percent required so that the entire >+ * height of the {@link #getScalableFigure() scalable figure} is visible on >+ * the screen. This is the zoom level associated with {@link #FIT_HEIGHT}. >+ * >+ * @return zoom setting required to fit the scalable figure vertically on >+ * the screen >+ */ >+ protected double getFitHeightZoomLevel() { >+ return getFitXZoomLevel(1); >+ } >+ >+ /** >+ * Calculates and returns the zoom percentage required to fit the entire >+ * {@link #getScalableFigure() scalable figure} on the screen. This is the >+ * zoom setting associated with {@link #FIT_ALL}. It is the minimum of >+ * {@link #getFitHeightZoomLevel()} and {@link #getFitWidthZoomLevel()}. >+ * >+ * @return zoom setting required to fit the entire scalable figure on the >+ * screen >+ */ >+ protected double getFitPageZoomLevel() { >+ return getFitXZoomLevel(2); >+ } >+ >+ /** >+ * Calculates and returns the zoom percentage required so that the entire >+ * width of the {@link #getScalableFigure() scalable figure} is visible on >+ * the screen. This is the zoom setting associated with {@link #FIT_WIDTH}. >+ * >+ * @return zoom setting required to fit the scalable figure horizontally on >+ * the screen >+ */ >+ protected double getFitWidthZoomLevel() { >+ return getFitXZoomLevel(0); >+ } >+ >+ /** >+ * Returns the maxZoom. >+ * >+ * @return double >+ */ >+ public double getMaxZoom() { >+ return getZoomLevels()[getZoomLevels().length - 1]; >+ } >+ >+ /** >+ * Returns the minZoom. >+ * >+ * @return double >+ */ >+ public double getMinZoom() { >+ return getZoomLevels()[0]; >+ } >+ >+ /** >+ * Returns the mutltiplier. This value is used to use zoom levels internally >+ * that are proportionally different than those displayed to the user. e.g. >+ * with a multiplier value of 2.0, the zoom level 1.0 will be displayed as >+ * "200%". >+ * >+ * @return double The multiplier >+ */ >+ public double getUIMultiplier() { >+ return multiplier; >+ } >+ >+ /** >+ * Returns the zoom level that is one level higher than the current level. >+ * If zoom level is at maximum, returns the maximum. >+ * >+ * @return double The next zoom level >+ */ >+ public double getNextZoomLevel() { >+ for (int i = 0; i < zoomLevels.length; i++) { >+ if (zoomLevels[i] > zoom) { >+ return zoomLevels[i]; >+ } >+ } >+ return getMaxZoom(); >+ } >+ >+ /** >+ * Returns the zoom level that is one level higher than the current level. >+ * If zoom level is at maximum, returns the maximum. >+ * >+ * @return double The previous zoom level >+ */ >+ public double getPreviousZoomLevel() { >+ for (int i = 1; i < zoomLevels.length; i++) { >+ if (zoomLevels[i] >= zoom) { >+ return zoomLevels[i - 1]; >+ } >+ } >+ return getMinZoom(); >+ } >+ >+ /** >+ * Returns the figure which performs the actual zooming. >+ * >+ * @return the scalable figure >+ */ >+ public ScalableFigure getScalableFigure() { >+ return pane; >+ } >+ >+ /** >+ * Returns the viewport. >+ * >+ * @return Viewport >+ */ >+ public Viewport getViewport() { >+ return viewport; >+ } >+ >+ /** >+ * Returns the current zoom level. >+ * >+ * @return double the zoom level >+ */ >+ public double getZoom() { >+ return zoom; >+ } >+ >+ private String format(double d) { >+ return "" + ((int) (d * 100)) + "%"; >+ } >+ >+ /** >+ * Returns the current zoom level as a percentage formatted String >+ * >+ * @return String The current zoom level as a String >+ */ >+ public String getZoomAsText() { >+ if (currentZoomContant != null) { >+ return currentZoomContant; >+ } >+ >+ // String newItem = format.format(zoom * multiplier); >+ String newItem = format(zoom * multiplier); >+ return newItem; >+ } >+ >+ /** >+ * Returns the list of strings that should be appended to the list of >+ * numerical zoom levels. These could be things such as Fit Width, Fit Page, >+ * etc. May return <code>null</code>. >+ * >+ * @return the list of contributed zoom levels >+ */ >+ public List getZoomLevelContributions() { >+ return zoomLevelContributions; >+ } >+ >+ /** >+ * Returns the zoomLevels. >+ * >+ * @return double[] >+ */ >+ public double[] getZoomLevels() { >+ return zoomLevels; >+ } >+ >+ /** >+ * Returns the list of zoom levels as Strings in percent notation, plus any >+ * additional zoom levels that were contributed using >+ * {@link #setZoomLevelContributions(List)}. >+ * >+ * @return List The list of zoom levels >+ */ >+ public String[] getZoomLevelsAsText() { >+ String[] zoomLevelStrings = new String[zoomLevels.length >+ + zoomLevelContributions.size()]; >+ >+ if (zoomLevelContributions != null) { >+ for (int i = 0; i < zoomLevelContributions.size(); i++) { >+ zoomLevelStrings[i] = (String) zoomLevelContributions.get(i); >+ } >+ } >+ for (int i = 0; i < zoomLevels.length; i++) { >+ // zoomLevelStrings[i + zoomLevelContributions.size()] = >+ // format.format(zoomLevels[i] * multiplier); >+ zoomLevelStrings[i + zoomLevelContributions.size()] = format(zoomLevels[i] >+ * multiplier); >+ } >+ >+ return zoomLevelStrings; >+ } >+ >+ /** >+ * Sets the zoom level to the given value. Min-max range check is not done. >+ * >+ * @param zoom >+ * the new zoom level >+ */ >+ protected void primSetZoom(double zoom) { >+ Point p1 = getViewport().getClientArea().getCenter(); >+ Point p2 = p1.getCopy(); >+ Point p = getViewport().getViewLocation(); >+ double prevZoom = this.zoom; >+ this.zoom = zoom; >+ pane.setScale(zoom); >+ fireZoomChanged(); >+ getViewport().validate(); >+ >+ p2.scale(zoom / prevZoom); >+ Dimension dif = p2.getDifference(p1); >+ p.x += dif.width; >+ p.y += dif.height; >+ setViewLocation(p); >+ } >+ >+ /** >+ * Removes the given ZoomListener from this ZoomManager's list of listeners. >+ * >+ * @param listener >+ * the ZoomListener to be removed >+ */ >+ public void removeZoomListener(ZoomListener listener) { >+ listeners.remove(listener); >+ } >+ >+ /** >+ * Sets the UI multiplier. The UI multiplier is applied to all zoom settings >+ * when they are presented to the user ({@link #getZoomAsText()}). >+ * Similarly, the multiplier is inversely applied when the user specifies a >+ * zoom level ({@link #setZoomAsText(String)}). >+ * <P> >+ * When the UI multiplier is <code>1.0</code>, the User will see the exact >+ * zoom level that is being applied. If the value is <code>2.0</code>, then >+ * a scale of <code>0.5</code> will be labeled "100%" to the User. >+ * >+ * @param multiplier >+ * The mutltiplier to set >+ */ >+ public void setUIMultiplier(double multiplier) { >+ this.multiplier = multiplier; >+ } >+ >+ /** >+ * Sets the Viewport's view associated with this ZoomManager to the passed >+ * Point >+ * >+ * @param p >+ * The new location for the Viewport's view. >+ */ >+ public void setViewLocation(Point p) { >+ viewport.setViewLocation(p.x, p.y); >+ >+ } >+ >+ /** >+ * Sets the zoom level to the given value. If the zoom is out of the min-max >+ * range, it will be ignored. >+ * >+ * @param zoom >+ * the new zoom level >+ */ >+ public void setZoom(double zoom) { >+ currentZoomContant = null; >+ zoom = Math.min(getMaxZoom(), zoom); >+ zoom = Math.max(getMinZoom(), zoom); >+ if (this.zoom != zoom) { >+ primSetZoom(zoom); >+ } >+ } >+ >+ /** >+ * Sets which zoom methods get animated. >+ * >+ * @param style >+ * the style bits determining the zoom methods to be animated. >+ */ >+ public void setZoomAnimationStyle(int style) { >+ // zoomAnimationStyle = style; >+ } >+ >+ /** >+ * Sets zoom to the passed string. The string must be composed of numeric >+ * characters only with the exception of a decimal point and a '%' as the >+ * last character. If the zoom level contribution list has been set, this >+ * method should be overridden to provide the appropriate zoom >+ * implementation for the new zoom levels. >+ * >+ * @param zoomString >+ * The new zoom level >+ */ >+ public void setZoomAsText(String zoomString) { >+ currentZoomContant = null; >+ if (zoomString.equalsIgnoreCase(FIT_HEIGHT)) { >+ currentZoomContant = FIT_HEIGHT; >+ primSetZoom(getFitHeightZoomLevel()); >+ viewport.getUpdateManager().performUpdate(); >+ viewport.setViewLocation(viewport.getHorizontalRangeModel() >+ .getValue(), viewport.getVerticalRangeModel().getMinimum()); >+ } else if (zoomString.equalsIgnoreCase(FIT_ALL)) { >+ currentZoomContant = FIT_ALL; >+ primSetZoom(getFitPageZoomLevel()); >+ viewport.getUpdateManager().performUpdate(); >+ viewport.setViewLocation(viewport.getHorizontalRangeModel() >+ .getMinimum(), viewport.getVerticalRangeModel() >+ .getMinimum()); >+ } else if (zoomString.equalsIgnoreCase(FIT_WIDTH)) { >+ currentZoomContant = FIT_WIDTH; >+ primSetZoom(getFitWidthZoomLevel()); >+ viewport.getUpdateManager().performUpdate(); >+ viewport.setViewLocation(viewport.getHorizontalRangeModel() >+ .getMinimum(), viewport.getVerticalRangeModel().getValue()); >+ } else { >+ try { >+ // Trim off the '%' >+ if (zoomString.charAt(zoomString.length() - 1) == '%') { >+ zoomString = zoomString.substring(0, >+ zoomString.length() - 1); >+ } >+ double newZoom = Double.parseDouble(zoomString) / 100; >+ setZoom(newZoom / multiplier); >+ } catch (Exception e) { >+ Display.getCurrent().beep(); >+ } >+ } >+ } >+ >+ /** >+ * Sets the list of zoom level contributions (as strings). If you contribute >+ * something <b>other than</b> {@link #FIT_HEIGHT}, {@link #FIT_WIDTH} and >+ * {@link #FIT_ALL} you must subclass this class and override this method to >+ * implement your contributed zoom function. >+ * >+ * @param contributions >+ * the list of contributed zoom levels >+ */ >+ public void setZoomLevelContributions(List contributions) { >+ zoomLevelContributions = contributions; >+ } >+ >+ /** >+ * Sets the zoomLevels. >+ * >+ * @param zoomLevels >+ * The zoomLevels to set >+ */ >+ public void setZoomLevels(double[] zoomLevels) { >+ this.zoomLevels = zoomLevels; >+ } >+ >+ /** >+ * Sets the zoom level to be one level higher >+ */ >+ public void zoomIn() { >+ setZoom(getNextZoomLevel()); >+ } >+ >+ /** >+ * Currently does nothing. >+ * >+ * @param rect >+ * a rectangle >+ */ >+ public void zoomTo(Rectangle rect) { >+ } >+ >+ // private void performAnimatedZoom(Rectangle rect, boolean zoomIn, int >+ // iterationCount) { >+ // double finalRatio; >+ // double zoomIncrement; >+ // >+ // if (zoomIn) { >+ // finalRatio = zoom / getNextZoomLevel(); >+ // zoomIncrement = (getNextZoomLevel() - zoom) / iterationCount; >+ // } else { >+ // finalRatio = zoom / getPreviousZoomLevel(); >+ // zoomIncrement = (getPreviousZoomLevel() - zoom) / iterationCount; >+ // } >+ // >+ // getScalableFigure().translateToRelative(rect); >+ // Point originalViewLocation = getViewport().getViewLocation(); >+ // Point finalViewLocation = calculateViewLocation(rect, finalRatio); >+ // >+ // double xIncrement = >+ // (double) (finalViewLocation.x - originalViewLocation.x) / iterationCount; >+ // double yIncrement = >+ // (double) (finalViewLocation.y - originalViewLocation.y) / iterationCount; >+ // >+ // double originalZoom = zoom; >+ // Point currentViewLocation = new Point(); >+ // for (int i = 1; i < iterationCount; i++) { >+ // currentViewLocation.x = (int)(originalViewLocation.x + (xIncrement * i)); >+ // currentViewLocation.y = (int)(originalViewLocation.y + (yIncrement * i)); >+ // setZoom(originalZoom + zoomIncrement * i); >+ // getViewport().validate(); >+ // setViewLocation(currentViewLocation); >+ // getViewport().getUpdateManager().performUpdate(); >+ // } >+ // >+ // if (zoomIn) >+ // setZoom(getNextZoomLevel()); >+ // else >+ // setZoom(getPreviousZoomLevel()); >+ // >+ // getViewport().validate(); >+ // setViewLocation(finalViewLocation); >+ // } >+ // >+ // private Point calculateViewLocation(Rectangle zoomRect, double ratio) { >+ // Point viewLocation = new Point(); >+ // viewLocation.x = (int)(zoomRect.x / ratio); >+ // viewLocation.y = (int)(zoomRect.y / ratio); >+ // return viewLocation; >+ // } >+ >+ /** >+ * Sets the zoom level to be one level lower >+ */ >+ public void zoomOut() { >+ setZoom(getPreviousZoomLevel()); >+ } >+ >+} >Index: src/org/eclipse/zest/core/viewers/internal/AbstractStylingModelFactory.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/viewers/internal/AbstractStylingModelFactory.java,v >retrieving revision 1.13 >diff -u -r1.13 AbstractStylingModelFactory.java >--- src/org/eclipse/zest/core/viewers/internal/AbstractStylingModelFactory.java 24 Jul 2009 19:13:37 -0000 1.13 >+++ src/org/eclipse/zest/core/viewers/internal/AbstractStylingModelFactory.java 15 Mar 2010 03:48:56 -0000 >@@ -1,414 +1,430 @@ >-/******************************************************************************* >- * 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; >- } >- } >- if (sn == null) { >- sn = createNode(graph, source); >- } >- if (dn == null) { >- 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; >- } >-} >+/******************************************************************************* >+ * Copyright 2005, 2010, 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 >+ * Mateusz Matela <mateusz.matela@gmail.com> - Adapt Zest to changes in layout - https://bugs.eclipse.org/bugs/show_bug.cgi?id=283179 >+ ******************************************************************************/ >+package org.eclipse.zest.core.viewers.internal; >+ >+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.AbstractStructuredGraphViewer; >+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.GraphContainer; >+import org.eclipse.zest.core.widgets.GraphItem; >+import org.eclipse.zest.core.widgets.GraphNode; >+ >+/** >+ * 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; >+ >+ /** >+ * >+ */ >+ public AbstractStylingModelFactory(AbstractStructuredGraphViewer viewer) { >+ this.viewer = viewer; >+ this.connectionStyle = SWT.NONE; >+ this.nodeStyle = SWT.NONE; >+ } >+ >+ 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; >+ } >+ } >+ if (sn == null) { >+ sn = createNode(graph, source); >+ } >+ if (dn == null) { >+ 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( >+ (GraphContainer) node, childNodes[i]); >+ styleItem(childNode); >+ } >+ ((GraphContainer) 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; >+ } >+ >+ /** >+ * @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()); >+ } >+ >+ /** >+ * 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; >+ } >+} >Index: src/org/eclipse/zest/core/viewers/internal/GraphItemStyler.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/viewers/internal/GraphItemStyler.java,v >retrieving revision 1.12 >diff -u -r1.12 GraphItemStyler.java >--- src/org/eclipse/zest/core/viewers/internal/GraphItemStyler.java 4 May 2009 04:47:32 -0000 1.12 >+++ src/org/eclipse/zest/core/viewers/internal/GraphItemStyler.java 15 Mar 2010 03:48:56 -0000 >@@ -1,269 +1,279 @@ >-/******************************************************************************* >- * Copyright 2005-2006, 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 org.eclipse.draw2d.IFigure; >-import org.eclipse.jface.viewers.IBaseLabelProvider; >-import org.eclipse.jface.viewers.IColorProvider; >-import org.eclipse.jface.viewers.IFontProvider; >-import org.eclipse.jface.viewers.ILabelProvider; >-import org.eclipse.swt.SWT; >-import org.eclipse.swt.SWTError; >-import org.eclipse.swt.graphics.Color; >-import org.eclipse.zest.core.viewers.IConnectionStyleProvider; >-import org.eclipse.zest.core.viewers.IEntityConnectionStyleProvider; >-import org.eclipse.zest.core.viewers.IEntityStyleProvider; >-import org.eclipse.zest.core.viewers.ISelfStyleProvider; >-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.ZestStyles; >- >-/* >- * Helper class used to style graph elements based on graph element stylers. >- * >- * @author Del Myers >- */ >-// @tag bug(151327-Styles) : created to help resolve this bug >-public class GraphItemStyler { >- public static void styleItem(GraphItem item, final IBaseLabelProvider labelProvider) { >- >- if (item instanceof GraphNode) { >- GraphNode node = (GraphNode) item; >- // set defaults. >- if (node.getGraphModel().getNodeStyle() != ZestStyles.NONE) { >- node.setNodeStyle(node.getGraphModel().getNodeStyle()); >- } else { >- node.setNodeStyle(SWT.NONE); >- } >- Object entity = node.getData(); >- if (labelProvider instanceof IEntityStyleProvider) { >- styleNode(node, (IEntityStyleProvider) labelProvider); >- } >- if (labelProvider instanceof IColorProvider) { >- IColorProvider colorProvider = (IColorProvider) labelProvider; >- node.setForegroundColor(colorProvider.getForeground(entity)); >- node.setBackgroundColor(colorProvider.getBackground(entity)); >- } >- if (labelProvider instanceof IFontProvider) { >- IFontProvider fontProvider = (IFontProvider) labelProvider; >- node.setFont(fontProvider.getFont(entity)); >- } >- if (labelProvider instanceof ILabelProvider) { >- String text = ((ILabelProvider) labelProvider).getText(node.getData()); >- node.setText((text != null) ? text : ""); >- node.setImage(((ILabelProvider) labelProvider).getImage(node.getData())); >- } >- if (labelProvider instanceof ISelfStyleProvider) { >- ((ISelfStyleProvider) labelProvider).selfStyleNode(entity, node); >- } >- } else if (item instanceof GraphConnection) { >- GraphConnection conn = (GraphConnection) item; >- >- // set defaults >- if (conn.getGraphModel().getConnectionStyle() != ZestStyles.NONE) { >- int s = conn.getGraphModel().getConnectionStyle(); >- conn.setConnectionStyle(s); >- } else { >- conn.setConnectionStyle(SWT.NONE); >- } >- if (labelProvider instanceof ILabelProvider) { >- String text = ((ILabelProvider) labelProvider).getText(conn.getExternalConnection()); >- conn.setText((text != null) ? text : ""); >- conn.setImage(((ILabelProvider) labelProvider).getImage(conn.getExternalConnection())); >- } >- if (labelProvider instanceof IEntityConnectionStyleProvider) { >- styleEntityConnection(conn, (IEntityConnectionStyleProvider) labelProvider); >- } else if (labelProvider instanceof IConnectionStyleProvider) { >- styleConnection(conn, (IConnectionStyleProvider) labelProvider); >- } >- int swt = getLineStyleForZestStyle(conn.getConnectionStyle()); >- conn.setLineStyle(swt); >- if (labelProvider instanceof ISelfStyleProvider) { >- ((ISelfStyleProvider) labelProvider).selfStyleConnection(conn.getData(), conn); >- } >- } >- } >- >- /** >- * @param conn >- * @param provider >- */ >- private static void styleConnection(GraphConnection conn, IConnectionStyleProvider provider) { >- Object rel = conn.getExternalConnection(); >- Color c; >- int style = provider.getConnectionStyle(rel); >- if (!ZestStyles.validateConnectionStyle(style)) { >- throw new SWTError(SWT.ERROR_INVALID_ARGUMENT); >- } >- if (style != ZestStyles.NONE) { >- conn.setConnectionStyle(style); >- } >- // @tag bug(152530-Bezier(fix)) >- // @tat TODO curves bezier: Add back the bezier connection stuff >- // if (ZestStyles.checkStyle(conn.getConnectionStyle(), >- // ZestStyles.CONNECTIONS_BEZIER) >- // && provider instanceof IConnectionStyleBezierExtension) { >- // IConnectionStyleBezierExtension bezier = >- // (IConnectionStyleBezierExtension) provider; >- // double d; >- // if (!Double.isNaN((d = bezier.getStartAngle(rel)))) { >- // conn.setStartAngle(d); >- // } >- // if (!Double.isNaN((d = bezier.getEndAngle(rel)))) { >- // conn.setEndAngle(d); >- // } >- // if (!Double.isNaN((d = bezier.getStartDistance(rel)))) { >- // conn.setStartLength(d); >- // } >- // if (!Double.isNaN((d = bezier.getEndDistance(rel)))) { >- // conn.setEndLength(d); >- // } >- // } >- if ((c = provider.getHighlightColor(rel)) != null) { >- conn.setHighlightColor(c); >- } >- if ((c = provider.getColor(rel)) != null) { >- conn.setLineColor(c); >- } >- IFigure tooltip; >- if ((tooltip = provider.getTooltip(rel)) != null) { >- conn.setTooltip(tooltip); >- } >- int w = -1; >- if ((w = provider.getLineWidth(rel)) >= 0) { >- conn.setLineWidth(w); >- } >- } >- >- /** >- * @param conn >- * @param provider >- */ >- private static void styleEntityConnection(GraphConnection conn, IEntityConnectionStyleProvider provider) { >- Object src = conn.getSource().getData(); >- Object dest = conn.getDestination().getData(); >- Color c; >- int style = provider.getConnectionStyle(src, dest); >- if (!ZestStyles.validateConnectionStyle(style)) { >- throw new SWTError(SWT.ERROR_INVALID_ARGUMENT); >- } >- if (style != ZestStyles.NONE) { >- conn.setConnectionStyle(style); >- } >- // @tag bug(152530-Bezier(fisx)) >- // @tag TODO curved connections bezier : add back the bezier connection stuff >- // if (ZestStyles.checkStyle(conn.getConnectionStyle(), >- // ZestStyles.CONNECTIONS_BEZIER) >- // && provider instanceof IEntityConnectionStyleBezierExtension) { >- // IEntityConnectionStyleBezierExtension bezier = >- // (IEntityConnectionStyleBezierExtension) provider; >- // double d; >- // if (!Double.isNaN((d = bezier.getStartAngle(src, dest)))) { >- // conn.setStartAngle(d); >- // } >- // if (!Double.isNaN((d = bezier.getEndAngle(src, dest)))) { >- // conn.setEndAngle(d); >- // } >- // if (!Double.isNaN((d = bezier.getStartDistance(src, dest)))) { >- // conn.setStartLength(d); >- // } >- // if (!Double.isNaN((d = bezier.getEndDistance(src, dest)))) { >- // conn.setEndLength(d); >- // } >- // } >- if ((c = provider.getColor(src, dest)) != null) { >- conn.setLineColor(c); >- } >- if ((c = provider.getHighlightColor(src, dest)) != null) { >- conn.setHighlightColor(c); >- } >- int w = -1; >- if ((w = provider.getLineWidth(src, dest)) >= 0) { >- conn.setLineWidth(w); >- } >- } >- >- /** >- * Styles the given node according to the properties in the style provider. >- * >- * @param node >- * the graph element to style. >- * @param data >- * the element that is being styled. >- * @param provider >- * the style provier. >- */ >- // @tag bug(151327-Styles) : resolution >- private static void styleNode(GraphNode node, IEntityStyleProvider provider) { >- Object entity = node.getData(); >- // @tag ADJACENT : Removed highlight adjacent >- //node.setHighlightAdjacentNodes(provider.highlightAdjacentEntities(entity)); >- >- // @tag ADJACENT : Removed highlight adjacent >- /* >- if (provider.highlightAdjacentEntities(entity)) { >- Color c = provider.getAdjacentEntityHighlightColor(entity); >- if (c != null) { >- node.setHighlightAdjacentColor(c); >- } >- } >- */ >- Color c; >- IFigure figure; >- int width = -1; >- if (provider.fisheyeNode(entity) == true) { >- node.setNodeStyle(node.getNodeStyle() | ZestStyles.NODES_FISHEYE); >- } >- if ((c = provider.getBorderColor(entity)) != null) { >- node.setBorderColor(c); >- } >- if ((c = provider.getBorderHighlightColor(entity)) != null) { >- node.setBorderHighlightColor(c); >- } >- if ((c = provider.getNodeHighlightColor(entity)) != null) { >- node.setHighlightColor(c); >- } >- if ((c = provider.getBackgroundColour(entity)) != null) { >- node.setBackgroundColor(c); >- } >- if ((c = provider.getForegroundColour(entity)) != null) { >- node.setForegroundColor(c); >- } >- if ((width = provider.getBorderWidth(entity)) >= 0) { >- node.setBorderWidth(width); >- } >- if ((figure = provider.getTooltip(entity)) != null) { >- node.setTooltip(figure); >- } >- >- } >- >- /** >- * Returns the SWT line style for the given zest connection style. >- * >- */ >- public static int getLineStyleForZestStyle(int style) { >- int lineStyles = ZestStyles.CONNECTIONS_DASH_DOT | ZestStyles.CONNECTIONS_DASH | ZestStyles.CONNECTIONS_DOT | ZestStyles.CONNECTIONS_SOLID; >- style = style & lineStyles; >- if (style == 0) { >- style = ZestStyles.CONNECTIONS_SOLID; >- } >- switch (style) { >- case ZestStyles.CONNECTIONS_DASH_DOT: >- return SWT.LINE_DASHDOT; >- case ZestStyles.CONNECTIONS_DASH: >- return SWT.LINE_DASH; >- case ZestStyles.CONNECTIONS_DOT: >- return SWT.LINE_DOT; >- } >- return SWT.LINE_SOLID; >- } >-} >+/******************************************************************************* >+ * Copyright 2005-2010, 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 org.eclipse.draw2d.IFigure; >+import org.eclipse.jface.viewers.IBaseLabelProvider; >+import org.eclipse.jface.viewers.IColorProvider; >+import org.eclipse.jface.viewers.IFontProvider; >+import org.eclipse.jface.viewers.ILabelProvider; >+import org.eclipse.swt.SWT; >+import org.eclipse.swt.SWTError; >+import org.eclipse.swt.graphics.Color; >+import org.eclipse.zest.core.viewers.IConnectionStyleProvider; >+import org.eclipse.zest.core.viewers.IEntityConnectionStyleProvider; >+import org.eclipse.zest.core.viewers.IEntityStyleProvider; >+import org.eclipse.zest.core.viewers.ISelfStyleProvider; >+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.ZestStyles; >+ >+/* >+ * Helper class used to style graph elements based on graph element stylers. >+ * >+ * @author Del Myers >+ */ >+// @tag bug(151327-Styles) : created to help resolve this bug >+public class GraphItemStyler { >+ public static void styleItem(GraphItem item, >+ final IBaseLabelProvider labelProvider) { >+ >+ if (item instanceof GraphNode) { >+ GraphNode node = (GraphNode) item; >+ // set defaults. >+ if (node.getGraphModel().getNodeStyle() != ZestStyles.NONE) { >+ node.setNodeStyle(node.getGraphModel().getNodeStyle()); >+ } else { >+ node.setNodeStyle(SWT.NONE); >+ } >+ Object entity = node.getData(); >+ if (labelProvider instanceof IEntityStyleProvider) { >+ styleNode(node, (IEntityStyleProvider) labelProvider); >+ } >+ if (labelProvider instanceof IColorProvider) { >+ IColorProvider colorProvider = (IColorProvider) labelProvider; >+ node.setForegroundColor(colorProvider.getForeground(entity)); >+ node.setBackgroundColor(colorProvider.getBackground(entity)); >+ } >+ if (labelProvider instanceof IFontProvider) { >+ IFontProvider fontProvider = (IFontProvider) labelProvider; >+ node.setFont(fontProvider.getFont(entity)); >+ } >+ if (labelProvider instanceof ILabelProvider) { >+ String text = ((ILabelProvider) labelProvider).getText(node >+ .getData()); >+ node.setText((text != null) ? text : ""); >+ node.setImage(((ILabelProvider) labelProvider).getImage(node >+ .getData())); >+ } >+ if (labelProvider instanceof ISelfStyleProvider) { >+ ((ISelfStyleProvider) labelProvider) >+ .selfStyleNode(entity, node); >+ } >+ } else if (item instanceof GraphConnection) { >+ GraphConnection conn = (GraphConnection) item; >+ >+ // set defaults >+ if (conn.getGraphModel().getConnectionStyle() != ZestStyles.NONE) { >+ int s = conn.getGraphModel().getConnectionStyle(); >+ conn.setConnectionStyle(s); >+ } else { >+ conn.setConnectionStyle(SWT.NONE); >+ } >+ if (labelProvider instanceof ILabelProvider) { >+ String text = ((ILabelProvider) labelProvider).getText(conn >+ .getExternalConnection()); >+ conn.setText((text != null) ? text : ""); >+ conn.setImage(((ILabelProvider) labelProvider).getImage(conn >+ .getExternalConnection())); >+ } >+ if (labelProvider instanceof IEntityConnectionStyleProvider) { >+ styleEntityConnection(conn, >+ (IEntityConnectionStyleProvider) labelProvider); >+ } else if (labelProvider instanceof IConnectionStyleProvider) { >+ styleConnection(conn, (IConnectionStyleProvider) labelProvider); >+ } >+ int swt = getLineStyleForZestStyle(conn.getConnectionStyle()); >+ conn.setLineStyle(swt); >+ if (labelProvider instanceof ISelfStyleProvider) { >+ ((ISelfStyleProvider) labelProvider).selfStyleConnection(conn >+ .getData(), conn); >+ } >+ } >+ } >+ >+ /** >+ * @param conn >+ * @param provider >+ */ >+ private static void styleConnection(GraphConnection conn, >+ IConnectionStyleProvider provider) { >+ Object rel = conn.getExternalConnection(); >+ Color c; >+ int style = provider.getConnectionStyle(rel); >+ if (!ZestStyles.validateConnectionStyle(style)) { >+ throw new SWTError(SWT.ERROR_INVALID_ARGUMENT); >+ } >+ if (style != ZestStyles.NONE) { >+ conn.setConnectionStyle(style); >+ } >+ // @tag bug(152530-Bezier(fix)) >+ // @tat TODO curves bezier: Add back the bezier connection stuff >+ // if (ZestStyles.checkStyle(conn.getConnectionStyle(), >+ // ZestStyles.CONNECTIONS_BEZIER) >+ // && provider instanceof IConnectionStyleBezierExtension) { >+ // IConnectionStyleBezierExtension bezier = >+ // (IConnectionStyleBezierExtension) provider; >+ // double d; >+ // if (!Double.isNaN((d = bezier.getStartAngle(rel)))) { >+ // conn.setStartAngle(d); >+ // } >+ // if (!Double.isNaN((d = bezier.getEndAngle(rel)))) { >+ // conn.setEndAngle(d); >+ // } >+ // if (!Double.isNaN((d = bezier.getStartDistance(rel)))) { >+ // conn.setStartLength(d); >+ // } >+ // if (!Double.isNaN((d = bezier.getEndDistance(rel)))) { >+ // conn.setEndLength(d); >+ // } >+ // } >+ if ((c = provider.getHighlightColor(rel)) != null) { >+ conn.setHighlightColor(c); >+ } >+ if ((c = provider.getColor(rel)) != null) { >+ conn.setLineColor(c); >+ } >+ IFigure tooltip; >+ if ((tooltip = provider.getTooltip(rel)) != null) { >+ conn.setTooltip(tooltip); >+ } >+ int w = -1; >+ if ((w = provider.getLineWidth(rel)) >= 0) { >+ conn.setLineWidth(w); >+ } >+ } >+ >+ /** >+ * @param conn >+ * @param provider >+ */ >+ private static void styleEntityConnection(GraphConnection conn, >+ IEntityConnectionStyleProvider provider) { >+ Object src = conn.getSource().getData(); >+ Object dest = conn.getDestination().getData(); >+ Color c; >+ int style = provider.getConnectionStyle(src, dest); >+ if (!ZestStyles.validateConnectionStyle(style)) { >+ throw new SWTError(SWT.ERROR_INVALID_ARGUMENT); >+ } >+ if (style != ZestStyles.NONE) { >+ conn.setConnectionStyle(style); >+ } >+ // @tag bug(152530-Bezier(fisx)) >+ // @tag TODO curved connections bezier : add back the bezier connection >+ // stuff >+ // if (ZestStyles.checkStyle(conn.getConnectionStyle(), >+ // ZestStyles.CONNECTIONS_BEZIER) >+ // && provider instanceof IEntityConnectionStyleBezierExtension) { >+ // IEntityConnectionStyleBezierExtension bezier = >+ // (IEntityConnectionStyleBezierExtension) provider; >+ // double d; >+ // if (!Double.isNaN((d = bezier.getStartAngle(src, dest)))) { >+ // conn.setStartAngle(d); >+ // } >+ // if (!Double.isNaN((d = bezier.getEndAngle(src, dest)))) { >+ // conn.setEndAngle(d); >+ // } >+ // if (!Double.isNaN((d = bezier.getStartDistance(src, dest)))) { >+ // conn.setStartLength(d); >+ // } >+ // if (!Double.isNaN((d = bezier.getEndDistance(src, dest)))) { >+ // conn.setEndLength(d); >+ // } >+ // } >+ if ((c = provider.getColor(src, dest)) != null) { >+ conn.setLineColor(c); >+ } >+ if ((c = provider.getHighlightColor(src, dest)) != null) { >+ conn.setHighlightColor(c); >+ } >+ int w = -1; >+ if ((w = provider.getLineWidth(src, dest)) >= 0) { >+ conn.setLineWidth(w); >+ } >+ } >+ >+ /** >+ * Styles the given node according to the properties in the style provider. >+ * >+ * @param node >+ * the graph element to style. >+ * @param data >+ * the element that is being styled. >+ * @param provider >+ * the style provier. >+ */ >+ // @tag bug(151327-Styles) : resolution >+ private static void styleNode(GraphNode node, IEntityStyleProvider provider) { >+ Object entity = node.getData(); >+ // @tag ADJACENT : Removed highlight adjacent >+ // node.setHighlightAdjacentNodes(provider.highlightAdjacentEntities(entity)); >+ >+ // @tag ADJACENT : Removed highlight adjacent >+ /* >+ * if (provider.highlightAdjacentEntities(entity)) { Color c = >+ * provider.getAdjacentEntityHighlightColor(entity); if (c != null) { >+ * node.setHighlightAdjacentColor(c); } } >+ */ >+ Color c; >+ IFigure figure; >+ int width = -1; >+ if (provider.fisheyeNode(entity) == true) { >+ node.setNodeStyle(node.getNodeStyle() | ZestStyles.NODES_FISHEYE); >+ } >+ if ((c = provider.getBorderColor(entity)) != null) { >+ node.setBorderColor(c); >+ } >+ if ((c = provider.getBorderHighlightColor(entity)) != null) { >+ node.setBorderHighlightColor(c); >+ } >+ if ((c = provider.getNodeHighlightColor(entity)) != null) { >+ node.setHighlightColor(c); >+ } >+ if ((c = provider.getBackgroundColour(entity)) != null) { >+ node.setBackgroundColor(c); >+ } >+ if ((c = provider.getForegroundColour(entity)) != null) { >+ node.setForegroundColor(c); >+ } >+ if ((width = provider.getBorderWidth(entity)) >= 0) { >+ node.setBorderWidth(width); >+ } >+ if ((figure = provider.getTooltip(entity)) != null) { >+ node.setTooltip(figure); >+ } >+ >+ } >+ >+ /** >+ * Returns the SWT line style for the given zest connection style. >+ * >+ */ >+ public static int getLineStyleForZestStyle(int style) { >+ int lineStyles = ZestStyles.CONNECTIONS_DASH_DOT >+ | ZestStyles.CONNECTIONS_DASH | ZestStyles.CONNECTIONS_DOT >+ | ZestStyles.CONNECTIONS_SOLID; >+ style = style & lineStyles; >+ if (style == 0) { >+ style = ZestStyles.CONNECTIONS_SOLID; >+ } >+ switch (style) { >+ case ZestStyles.CONNECTIONS_DASH_DOT: >+ return SWT.LINE_DASHDOT; >+ case ZestStyles.CONNECTIONS_DASH: >+ return SWT.LINE_DASH; >+ case ZestStyles.CONNECTIONS_DOT: >+ return SWT.LINE_DOT; >+ } >+ return SWT.LINE_SOLID; >+ } >+} >Index: src/org/eclipse/zest/core/viewers/internal/GraphModelEntityFactory.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/viewers/internal/GraphModelEntityFactory.java,v >retrieving revision 1.8 >diff -u -r1.8 GraphModelEntityFactory.java >--- src/org/eclipse/zest/core/viewers/internal/GraphModelEntityFactory.java 31 Mar 2009 16:39:23 -0000 1.8 >+++ src/org/eclipse/zest/core/viewers/internal/GraphModelEntityFactory.java 15 Mar 2010 03:48:56 -0000 >@@ -1,214 +1,229 @@ >-/******************************************************************************* >- * Copyright 2005, 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.HashSet; >-import java.util.Iterator; >-import java.util.LinkedList; >-import java.util.List; >-import java.util.Set; >- >-import org.eclipse.zest.core.viewers.EntityConnectionData; >-import org.eclipse.zest.core.viewers.IFigureProvider; >-import org.eclipse.zest.core.viewers.IGraphEntityContentProvider; >-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; >- >-/* >- * >- * @author Ian Bull >- */ >-public class GraphModelEntityFactory extends AbstractStylingModelFactory { >- >- AbstractStructuredGraphViewer viewer = null; >- >- public GraphModelEntityFactory(AbstractStructuredGraphViewer viewer) { >- super(viewer); >- this.viewer = viewer; >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#createGraphModel() >- */ >- public Graph createGraphModel(Graph model) { >- doBuildGraph(model); >- return model; >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.zest.core.internal.graphmodel.AbstractStylingModelFactory#doBuildGraph(org.eclipse.zest.core.internal.graphmodel.GraphModel) >- */ >- protected void doBuildGraph(Graph model) { >- super.doBuildGraph(model); >- Object inputElement = getViewer().getInput(); >- Object entities[] = getContentProvider().getElements(inputElement); >- if (entities == null) { >- return; >- } >- for (int i = 0; i < entities.length; i++) { >- Object data = entities[i]; >- IFigureProvider figureProvider = null; >- if (getLabelProvider() instanceof IFigureProvider) { >- figureProvider = (IFigureProvider) getLabelProvider(); >- } >- if (!filterElement(inputElement, data)) { >- if (figureProvider != null) { >- createNode(model, data, figureProvider.getFigure(data)); >- } else { >- createNode(model, data); >- } >- } >- } >- >- // We may have other entities (such as children of containers) >- Set keySet = ((AbstractStructuredGraphViewer) getViewer()).getNodesMap().keySet(); >- entities = keySet.toArray(); >- >- for (int i = 0; i < entities.length; i++) { >- Object data = entities[i]; >- >- // If this element is filtered, continue to the next one. >- if (filterElement(inputElement, data)) { >- continue; >- } >- Object[] related = ((IGraphEntityContentProvider) getContentProvider()).getConnectedTo(data); >- >- if (related != null) { >- for (int j = 0; j < related.length; j++) { >- // if the node this node is connected to is filtered, >- // don't display this edge >- if (filterElement(inputElement, related[j])) { >- continue; >- } >- EntityConnectionData connectionData = new EntityConnectionData(data, related[j]); >- if (filterElement(inputElement, connectionData)) { >- continue; >- } >- createConnection(model, connectionData, data, related[j]); >- } >- } >- } >- } >- >- /* >- * (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, boolean refreshLabels) { >- if (element == null) { >- return; >- } >- GraphNode node = viewer.getGraphModelNode(element); >- if (node == null) { >- // check to make sure that the user didn't send us an edge. >- GraphConnection conn = viewer.getGraphModelConnection(element); >- if (conn != null) { >- // refresh on the connected nodes. >- refresh(graph, conn.getSource().getData(), refreshLabels); >- refresh(graph, conn.getDestination().getData(), refreshLabels); >- return; >- } >- } >- // can only refresh on nodes in this kind of factory. >- if (node == null) { >- // do nothing >- return; >- } >- reconnect(graph, element, refreshLabels); >- >- if (refreshLabels) { >- update(node); >- for (Iterator it = node.getSourceConnections().iterator(); it.hasNext();) { >- update((GraphItem) it.next()); >- } >- for (Iterator it = node.getTargetConnections().iterator(); it.hasNext();) { >- update((GraphItem) it.next()); >- } >- } >- } >- >- /** >- * @param graph >- * @param element >- * @param refreshLabels >- */ >- private void reconnect(Graph graph, Object element, boolean refreshLabels) { >- GraphNode node = viewer.getGraphModelNode(element); >- Object[] related = ((IGraphEntityContentProvider) getContentProvider()).getConnectedTo(element); >- List connections = node.getSourceConnections(); >- LinkedList toAdd = new LinkedList(); >- LinkedList toDelete = new LinkedList(); >- LinkedList toKeep = new LinkedList(); >- HashSet oldExternalConnections = new HashSet(); >- HashSet newExternalConnections = new HashSet(); >- for (Iterator it = connections.iterator(); it.hasNext();) { >- oldExternalConnections.add(((GraphConnection) it.next()).getExternalConnection()); >- } >- for (int i = 0; i < related.length; i++) { >- newExternalConnections.add(new EntityConnectionData(element, related[i])); >- } >- for (Iterator it = oldExternalConnections.iterator(); it.hasNext();) { >- Object next = it.next(); >- if (!newExternalConnections.contains(next)) { >- toDelete.add(next); >- } else { >- toKeep.add(next); >- } >- } >- for (Iterator it = newExternalConnections.iterator(); it.hasNext();) { >- Object next = it.next(); >- if (!oldExternalConnections.contains(next)) { >- toAdd.add(next); >- } >- } >- for (Iterator it = toDelete.iterator(); it.hasNext();) { >- viewer.removeGraphModelConnection(it.next()); >- } >- toDelete.clear(); >- LinkedList newNodeList = new LinkedList(); >- for (Iterator it = toAdd.iterator(); it.hasNext();) { >- EntityConnectionData data = (EntityConnectionData) it.next(); >- GraphNode dest = viewer.getGraphModelNode(data.dest); >- if (dest == null) { >- newNodeList.add(data.dest); >- } >- createConnection(graph, data, data.source, data.dest); >- } >- toAdd.clear(); >- if (refreshLabels) { >- for (Iterator i = toKeep.iterator(); i.hasNext();) { >- styleItem(viewer.getGraphModelConnection(i.next())); >- } >- } >- for (Iterator it = newNodeList.iterator(); it.hasNext();) { >- // refresh the new nodes so that we get a fully-up-to-date graph. >- refresh(graph, it.next()); >- } >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#refresh(org.eclipse.zest.core.internal.graphmodel.GraphModel, >- * java.lang.Object, boolean) >- */ >- public void refresh(Graph graph, Object element) { >- refresh(graph, element, false); >- } >- >-} >+/******************************************************************************* >+ * Copyright 2005-2010, 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.HashSet; >+import java.util.Iterator; >+import java.util.LinkedList; >+import java.util.List; >+import java.util.Set; >+ >+import org.eclipse.zest.core.viewers.AbstractStructuredGraphViewer; >+import org.eclipse.zest.core.viewers.EntityConnectionData; >+import org.eclipse.zest.core.viewers.IFigureProvider; >+import org.eclipse.zest.core.viewers.IGraphEntityContentProvider; >+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; >+ >+/* >+ * >+ * @author Ian Bull >+ */ >+public class GraphModelEntityFactory extends AbstractStylingModelFactory { >+ >+ AbstractStructuredGraphViewer viewer = null; >+ >+ public GraphModelEntityFactory(AbstractStructuredGraphViewer viewer) { >+ super(viewer); >+ this.viewer = viewer; >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @seeorg.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory# >+ * createGraphModel() >+ */ >+ public Graph createGraphModel(Graph model) { >+ doBuildGraph(model); >+ return model; >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see >+ * org.eclipse.zest.core.internal.graphmodel.AbstractStylingModelFactory >+ * #doBuildGraph(org.eclipse.zest.core.internal.graphmodel.GraphModel) >+ */ >+ protected void doBuildGraph(Graph model) { >+ super.doBuildGraph(model); >+ Object inputElement = getViewer().getInput(); >+ Object entities[] = getContentProvider().getElements(inputElement); >+ if (entities == null) { >+ return; >+ } >+ for (int i = 0; i < entities.length; i++) { >+ Object data = entities[i]; >+ IFigureProvider figureProvider = null; >+ if (getLabelProvider() instanceof IFigureProvider) { >+ figureProvider = (IFigureProvider) getLabelProvider(); >+ } >+ if (!filterElement(inputElement, data)) { >+ if (figureProvider != null) { >+ createNode(model, data, figureProvider.getFigure(data)); >+ } else { >+ createNode(model, data); >+ } >+ } >+ } >+ >+ // We may have other entities (such as children of containers) >+ Set keySet = ((AbstractStructuredGraphViewer) getViewer()) >+ .getNodesMap().keySet(); >+ entities = keySet.toArray(); >+ >+ for (int i = 0; i < entities.length; i++) { >+ Object data = entities[i]; >+ >+ // If this element is filtered, continue to the next one. >+ if (filterElement(inputElement, data)) { >+ continue; >+ } >+ Object[] related = ((IGraphEntityContentProvider) getContentProvider()) >+ .getConnectedTo(data); >+ >+ if (related != null) { >+ for (int j = 0; j < related.length; j++) { >+ // if the node this node is connected to is filtered, >+ // don't display this edge >+ if (filterElement(inputElement, related[j])) { >+ continue; >+ } >+ EntityConnectionData connectionData = new EntityConnectionData( >+ data, related[j]); >+ if (filterElement(inputElement, connectionData)) { >+ continue; >+ } >+ createConnection(model, connectionData, data, related[j]); >+ } >+ } >+ } >+ } >+ >+ /* >+ * (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, boolean refreshLabels) { >+ if (element == null) { >+ return; >+ } >+ GraphNode node = viewer.getGraphModelNode(element); >+ if (node == null) { >+ // check to make sure that the user didn't send us an edge. >+ GraphConnection conn = viewer.getGraphModelConnection(element); >+ if (conn != null) { >+ // refresh on the connected nodes. >+ refresh(graph, conn.getSource().getData(), refreshLabels); >+ refresh(graph, conn.getDestination().getData(), refreshLabels); >+ return; >+ } >+ } >+ // can only refresh on nodes in this kind of factory. >+ if (node == null) { >+ // do nothing >+ return; >+ } >+ reconnect(graph, element, refreshLabels); >+ >+ if (refreshLabels) { >+ update(node); >+ for (Iterator it = node.getSourceConnections().iterator(); it >+ .hasNext();) { >+ update((GraphItem) it.next()); >+ } >+ for (Iterator it = node.getTargetConnections().iterator(); it >+ .hasNext();) { >+ update((GraphItem) it.next()); >+ } >+ } >+ } >+ >+ /** >+ * @param graph >+ * @param element >+ * @param refreshLabels >+ */ >+ private void reconnect(Graph graph, Object element, boolean refreshLabels) { >+ GraphNode node = viewer.getGraphModelNode(element); >+ Object[] related = ((IGraphEntityContentProvider) getContentProvider()) >+ .getConnectedTo(element); >+ List connections = node.getSourceConnections(); >+ LinkedList toAdd = new LinkedList(); >+ LinkedList toDelete = new LinkedList(); >+ LinkedList toKeep = new LinkedList(); >+ HashSet oldExternalConnections = new HashSet(); >+ HashSet newExternalConnections = new HashSet(); >+ for (Iterator it = connections.iterator(); it.hasNext();) { >+ oldExternalConnections.add(((GraphConnection) it.next()) >+ .getExternalConnection()); >+ } >+ for (int i = 0; i < related.length; i++) { >+ newExternalConnections.add(new EntityConnectionData(element, >+ related[i])); >+ } >+ for (Iterator it = oldExternalConnections.iterator(); it.hasNext();) { >+ Object next = it.next(); >+ if (!newExternalConnections.contains(next)) { >+ toDelete.add(next); >+ } else { >+ toKeep.add(next); >+ } >+ } >+ for (Iterator it = newExternalConnections.iterator(); it.hasNext();) { >+ Object next = it.next(); >+ if (!oldExternalConnections.contains(next)) { >+ toAdd.add(next); >+ } >+ } >+ for (Iterator it = toDelete.iterator(); it.hasNext();) { >+ viewer.removeGraphModelConnection(it.next()); >+ } >+ toDelete.clear(); >+ LinkedList newNodeList = new LinkedList(); >+ for (Iterator it = toAdd.iterator(); it.hasNext();) { >+ EntityConnectionData data = (EntityConnectionData) it.next(); >+ GraphNode dest = viewer.getGraphModelNode(data.dest); >+ if (dest == null) { >+ newNodeList.add(data.dest); >+ } >+ createConnection(graph, data, data.source, data.dest); >+ } >+ toAdd.clear(); >+ if (refreshLabels) { >+ for (Iterator i = toKeep.iterator(); i.hasNext();) { >+ styleItem(viewer.getGraphModelConnection(i.next())); >+ } >+ } >+ for (Iterator it = newNodeList.iterator(); it.hasNext();) { >+ // refresh the new nodes so that we get a fully-up-to-date graph. >+ refresh(graph, it.next()); >+ } >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see >+ * org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#refresh >+ * (org.eclipse.zest.core.internal.graphmodel.GraphModel, java.lang.Object, >+ * boolean) >+ */ >+ public void refresh(Graph graph, Object element) { >+ refresh(graph, element, false); >+ } >+ >+} >Index: src/org/eclipse/zest/core/viewers/internal/GraphModelEntityRelationshipFactory.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/viewers/internal/GraphModelEntityRelationshipFactory.java,v >retrieving revision 1.7 >diff -u -r1.7 GraphModelEntityRelationshipFactory.java >--- src/org/eclipse/zest/core/viewers/internal/GraphModelEntityRelationshipFactory.java 30 Jan 2009 22:25:48 -0000 1.7 >+++ src/org/eclipse/zest/core/viewers/internal/GraphModelEntityRelationshipFactory.java 15 Mar 2010 03:48:56 -0000 >@@ -1,137 +1,152 @@ >-/******************************************************************************* >- * Copyright 2005-2006, 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.List; >- >-import org.eclipse.zest.core.viewers.IGraphEntityRelationshipContentProvider; >-import org.eclipse.zest.core.widgets.Graph; >-import org.eclipse.zest.core.widgets.GraphContainer; >-import org.eclipse.zest.core.widgets.GraphNode; >- >-/* >- * A factory for the IGraphEntityRelationshipContentProvider. >- * >- * @author Del Myers >- */ >-// @tag bug.154580-Content.fix >-// @tag bug.160367-Refreshing.fix : updated to use new >-// AbstractStylingModelFactory >-public class GraphModelEntityRelationshipFactory extends AbstractStylingModelFactory { >- >- public GraphModelEntityRelationshipFactory(AbstractStructuredGraphViewer viewer) { >- super(viewer); >- if (!(viewer.getContentProvider() instanceof IGraphEntityRelationshipContentProvider)) { >- throw new IllegalArgumentException("Expected IGraphEntityRelationshipContentProvider"); >- } >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.zest.core.internal.graphmodel.AbstractStylingModelFactory#createGraphModel() >- */ >- public Graph createGraphModel(Graph model) { >- doBuildGraph(model); >- return model; >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.zest.core.internal.graphmodel.AbstractStylingModelFactory#doBuildGraph(org.eclipse.zest.core.internal.graphmodel.GraphModel) >- */ >- protected void doBuildGraph(Graph model) { >- super.doBuildGraph(model); >- Object[] nodes = getContentProvider().getElements(getViewer().getInput()); >- nodes = filter(getViewer().getInput(), nodes); >- createModelNodes(model, nodes); >- createModelRelationships(model); >- } >- >- /** >- * Creates all the model relationships. Assumes that all of the model nodes >- * have been created in the graph model already. Runtime O(n^2) + O(r). >- * >- * @param model >- * the model to create the relationship on. >- */ >- private void createModelRelationships(Graph model) { >- GraphNode[] modelNodes = getNodesArray(model); >- List listOfNodes = new ArrayList(); >- for (int i = 0; i < modelNodes.length; i++) { >- listOfNodes.add(modelNodes[i]); >- } >- >- for (int i = 0; i < listOfNodes.size(); i++) { >- GraphNode node = (GraphNode) listOfNodes.get(i); >- if (node instanceof GraphContainer) { >- List childNodes = ((GraphContainer) node).getNodes(); >- listOfNodes.addAll(childNodes); >- } >- } >- modelNodes = (GraphNode[]) listOfNodes.toArray(new GraphNode[listOfNodes.size()]); >- >- IGraphEntityRelationshipContentProvider content = getCastedContent(); >- for (int i = 0; i < modelNodes.length; i++) { >- for (int j = 0; j < modelNodes.length; j++) { >- Object[] rels = content.getRelationships(modelNodes[i].getData(), modelNodes[j].getData()); >- if (rels != null) { >- rels = filter(getViewer().getInput(), rels); >- for (int r = 0; r < rels.length; r++) { >- createConnection(model, rels[r], modelNodes[i].getData(), modelNodes[j].getData()); >- } >- } >- } >- } >- } >- >- /** >- * Creates the model nodes for the given external nodes. >- * >- * @param model >- * the graph model. >- * @param nodes >- * the external nodes. >- */ >- private void createModelNodes(Graph model, Object[] nodes) { >- for (int i = 0; i < nodes.length; i++) { >- createNode(model, nodes[i]); >- } >- } >- >- /* >- * (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); >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#refresh(org.eclipse.zest.core.internal.graphmodel.GraphModel, >- * java.lang.Object, boolean) >- */ >- public void refresh(Graph graph, Object element, boolean updateLabels) { >- // with this kind of graph, it is just as easy and cost-effective to >- // rebuild the whole thing. >- refreshGraph(graph); >- } >- >- private IGraphEntityRelationshipContentProvider getCastedContent() { >- return (IGraphEntityRelationshipContentProvider) getContentProvider(); >- } >- >-} >+/******************************************************************************* >+ * Copyright 2005-2010, 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.List; >+ >+import org.eclipse.zest.core.viewers.AbstractStructuredGraphViewer; >+import org.eclipse.zest.core.viewers.IGraphEntityRelationshipContentProvider; >+import org.eclipse.zest.core.widgets.Graph; >+import org.eclipse.zest.core.widgets.GraphContainer; >+import org.eclipse.zest.core.widgets.GraphNode; >+ >+/* >+ * A factory for the IGraphEntityRelationshipContentProvider. >+ * >+ * @author Del Myers >+ */ >+// @tag bug.154580-Content.fix >+// @tag bug.160367-Refreshing.fix : updated to use new >+// AbstractStylingModelFactory >+public class GraphModelEntityRelationshipFactory extends >+ AbstractStylingModelFactory { >+ >+ public GraphModelEntityRelationshipFactory( >+ AbstractStructuredGraphViewer viewer) { >+ super(viewer); >+ if (!(viewer.getContentProvider() instanceof IGraphEntityRelationshipContentProvider)) { >+ throw new IllegalArgumentException( >+ "Expected IGraphEntityRelationshipContentProvider"); >+ } >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see >+ * org.eclipse.zest.core.internal.graphmodel.AbstractStylingModelFactory >+ * #createGraphModel() >+ */ >+ public Graph createGraphModel(Graph model) { >+ doBuildGraph(model); >+ return model; >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see >+ * org.eclipse.zest.core.internal.graphmodel.AbstractStylingModelFactory >+ * #doBuildGraph(org.eclipse.zest.core.internal.graphmodel.GraphModel) >+ */ >+ protected void doBuildGraph(Graph model) { >+ super.doBuildGraph(model); >+ Object[] nodes = getContentProvider().getElements( >+ getViewer().getInput()); >+ nodes = filter(getViewer().getInput(), nodes); >+ createModelNodes(model, nodes); >+ createModelRelationships(model); >+ } >+ >+ /** >+ * Creates all the model relationships. Assumes that all of the model nodes >+ * have been created in the graph model already. Runtime O(n^2) + O(r). >+ * >+ * @param model >+ * the model to create the relationship on. >+ */ >+ private void createModelRelationships(Graph model) { >+ GraphNode[] modelNodes = getNodesArray(model); >+ List listOfNodes = new ArrayList(); >+ for (int i = 0; i < modelNodes.length; i++) { >+ listOfNodes.add(modelNodes[i]); >+ } >+ >+ for (int i = 0; i < listOfNodes.size(); i++) { >+ GraphNode node = (GraphNode) listOfNodes.get(i); >+ if (node instanceof GraphContainer) { >+ List childNodes = ((GraphContainer) node).getNodes(); >+ listOfNodes.addAll(childNodes); >+ } >+ } >+ modelNodes = (GraphNode[]) listOfNodes >+ .toArray(new GraphNode[listOfNodes.size()]); >+ >+ IGraphEntityRelationshipContentProvider content = getCastedContent(); >+ for (int i = 0; i < modelNodes.length; i++) { >+ for (int j = 0; j < modelNodes.length; j++) { >+ Object[] rels = content.getRelationships(modelNodes[i] >+ .getData(), modelNodes[j].getData()); >+ if (rels != null) { >+ rels = filter(getViewer().getInput(), rels); >+ for (int r = 0; r < rels.length; r++) { >+ createConnection(model, rels[r], modelNodes[i] >+ .getData(), modelNodes[j].getData()); >+ } >+ } >+ } >+ } >+ } >+ >+ /** >+ * Creates the model nodes for the given external nodes. >+ * >+ * @param model >+ * the graph model. >+ * @param nodes >+ * the external nodes. >+ */ >+ private void createModelNodes(Graph model, Object[] nodes) { >+ for (int i = 0; i < nodes.length; i++) { >+ createNode(model, nodes[i]); >+ } >+ } >+ >+ /* >+ * (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); >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see >+ * org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#refresh >+ * (org.eclipse.zest.core.internal.graphmodel.GraphModel, java.lang.Object, >+ * boolean) >+ */ >+ public void refresh(Graph graph, Object element, boolean updateLabels) { >+ // with this kind of graph, it is just as easy and cost-effective to >+ // rebuild the whole thing. >+ refreshGraph(graph); >+ } >+ >+ private IGraphEntityRelationshipContentProvider getCastedContent() { >+ return (IGraphEntityRelationshipContentProvider) getContentProvider(); >+ } >+ >+} >Index: src/org/eclipse/zest/core/viewers/internal/GraphModelFactory.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/viewers/internal/GraphModelFactory.java,v >retrieving revision 1.6 >diff -u -r1.6 GraphModelFactory.java >--- src/org/eclipse/zest/core/viewers/internal/GraphModelFactory.java 31 Mar 2009 16:39:23 -0000 1.6 >+++ src/org/eclipse/zest/core/viewers/internal/GraphModelFactory.java 15 Mar 2010 03:48:56 -0000 >@@ -1,171 +1,181 @@ >-/******************************************************************************* >- * Copyright 2005, 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.Iterator; >-import java.util.List; >- >-import org.eclipse.zest.core.viewers.IFigureProvider; >-import org.eclipse.zest.core.viewers.IGraphContentProvider; >-import org.eclipse.zest.core.widgets.Graph; >-import org.eclipse.zest.core.widgets.GraphConnection; >-import org.eclipse.zest.core.widgets.GraphNode; >- >-/** >- * This factory helps make models (nodes & connections). >- * >- * @author Ian Bull >- * @author Chris Callendar >- */ >-public class GraphModelFactory extends AbstractStylingModelFactory { >- >- AbstractStructuredGraphViewer viewer = null; >- >- public GraphModelFactory(AbstractStructuredGraphViewer viewer) { >- super(viewer); >- this.viewer = viewer; >- } >- >- /* >- * (non-Javadoc) >- * >- * @see ca.uvic.cs.zest.internal.graphmodel.IGraphModelFactory#createModel() >- */ >- public Graph createGraphModel(Graph model) { >- doBuildGraph(model); >- return model; >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.zest.core.internal.graphmodel.AbstractStylingModelFactory#doBuildGraph(org.eclipse.zest.core.internal.graphmodel.GraphModel) >- */ >- protected void doBuildGraph(Graph model) { >- super.doBuildGraph(model); >- // make the model have the same styles as the viewer >- Object rels[] = getContentProvider().getElements(getViewer().getInput()); >- if (rels != null) { >- IFigureProvider figureProvider = null; >- if (getLabelProvider() instanceof IFigureProvider) { >- figureProvider = (IFigureProvider) getLabelProvider(); >- } >- >- // If rels returns null then just continue >- // @tag zest(bug(134928(fix))) : An empty graph causes an NPE >- for (int i = 0; i < rels.length; i++) { >- // Check the filter on the source >- Object source = getCastedContent().getSource(rels[i]); >- source = filterElement(getViewer().getInput(), source) ? null : source; >- >- // Check hte filter on the dest >- Object dest = getCastedContent().getDestination(rels[i]); >- dest = filterElement(getViewer().getInput(), dest) ? null : dest; >- >- if (source == null) { >- // just create the node for the destination >- if (dest != null) { >- if (figureProvider != null) { >- createNode(model, dest, figureProvider.getFigure(dest)); >- } else { >- createNode(model, dest); >- } >- } >- continue; >- } else if (dest == null) { >- // just create the node for the source >- if (source != null) { >- if (figureProvider != null) { >- createNode(model, source, figureProvider.getFigure(dest)); >- } else { >- createNode(model, source); >- } >- } >- continue; >- } >- // If any of the source, dest is null or the edge is filtered, >- // don't create the graph. >- if (source != null && dest != null && !filterElement(getViewer().getInput(), rels[i])) { >- createConnection(model, rels[i], getCastedContent().getSource(rels[i]), getCastedContent().getDestination(rels[i])); >- } >- } >- } >- >- } >- >- private IGraphContentProvider getCastedContent() { >- return (IGraphContentProvider) getContentProvider(); >- } >- >- /* >- * (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); >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#refresh(org.eclipse.zest.core.internal.graphmodel.GraphModel, >- * java.lang.Object, boolean) >- */ >- public void refresh(Graph graph, Object element, boolean updateLabels) { >- GraphConnection conn = viewer.getGraphModelConnection(element); >- if (conn == null) { >- // did the user send us a node? Check all of the connections on the >- // node. >- GraphNode node = viewer.getGraphModelNode(element); >- if (node != null) { >- List connections = node.getSourceConnections(); >- for (Iterator it = connections.iterator(); it.hasNext();) { >- GraphConnection c = (GraphConnection) it.next(); >- refresh(graph, c.getExternalConnection(), updateLabels); >- } >- connections = node.getTargetConnections(); >- for (Iterator it = connections.iterator(); it.hasNext();) { >- GraphConnection c = (GraphConnection) it.next(); >- refresh(graph, c.getExternalConnection(), updateLabels); >- } >- } >- return; >- } >- Object oldSource = conn.getSource().getData(); >- Object oldDest = conn.getDestination().getData(); >- Object newSource = getCastedContent().getSource(element); >- Object newDest = getCastedContent().getDestination(element); >- if (!(oldSource.equals(newSource) && oldDest.equals(newDest))) { >- GraphNode internalSource = viewer.getGraphModelNode(newSource); >- GraphNode internalDest = viewer.getGraphModelNode(newDest); >- if (internalSource == null) { >- internalSource = createNode(graph, newSource); >- } else if (updateLabels) { >- styleItem(internalSource); >- } >- if (internalDest == null) { >- internalDest = createNode(graph, newDest); >- } else if (updateLabels) { >- styleItem(internalDest); >- } >- >- // @tag TODO: Remove these lines >- // conn.disconnect(); >- // conn.reconnect(internalSource, internalDest); >- if (updateLabels) { >- styleItem(conn); >- } >- } >- >- } >- >-} >+/******************************************************************************* >+ * Copyright 2005-2010, 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.Iterator; >+import java.util.List; >+ >+import org.eclipse.zest.core.viewers.AbstractStructuredGraphViewer; >+import org.eclipse.zest.core.viewers.IFigureProvider; >+import org.eclipse.zest.core.viewers.IGraphContentProvider; >+import org.eclipse.zest.core.widgets.Graph; >+import org.eclipse.zest.core.widgets.GraphConnection; >+import org.eclipse.zest.core.widgets.GraphNode; >+ >+/** >+ * This factory helps make models (nodes & connections). >+ * >+ * @author Ian Bull >+ * @author Chris Callendar >+ */ >+public class GraphModelFactory extends AbstractStylingModelFactory { >+ >+ AbstractStructuredGraphViewer viewer = null; >+ >+ public GraphModelFactory(AbstractStructuredGraphViewer viewer) { >+ super(viewer); >+ this.viewer = viewer; >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see ca.uvic.cs.zest.internal.graphmodel.IGraphModelFactory#createModel() >+ */ >+ public Graph createGraphModel(Graph model) { >+ doBuildGraph(model); >+ return model; >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see >+ * org.eclipse.zest.core.internal.graphmodel.AbstractStylingModelFactory >+ * #doBuildGraph(org.eclipse.zest.core.internal.graphmodel.GraphModel) >+ */ >+ protected void doBuildGraph(Graph model) { >+ super.doBuildGraph(model); >+ // make the model have the same styles as the viewer >+ Object rels[] = getContentProvider() >+ .getElements(getViewer().getInput()); >+ if (rels != null) { >+ IFigureProvider figureProvider = null; >+ if (getLabelProvider() instanceof IFigureProvider) { >+ figureProvider = (IFigureProvider) getLabelProvider(); >+ } >+ >+ // If rels returns null then just continue >+ // @tag zest(bug(134928(fix))) : An empty graph causes an NPE >+ for (int i = 0; i < rels.length; i++) { >+ // Check the filter on the source >+ Object source = getCastedContent().getSource(rels[i]); >+ source = filterElement(getViewer().getInput(), source) ? null >+ : source; >+ >+ // Check hte filter on the dest >+ Object dest = getCastedContent().getDestination(rels[i]); >+ dest = filterElement(getViewer().getInput(), dest) ? null >+ : dest; >+ >+ if (source == null) { >+ // just create the node for the destination >+ if (dest != null) { >+ if (figureProvider != null) { >+ createNode(model, dest, figureProvider >+ .getFigure(dest)); >+ } else { >+ createNode(model, dest); >+ } >+ } >+ continue; >+ } else if (dest == null) { >+ // just create the node for the source >+ if (source != null) { >+ if (figureProvider != null) { >+ createNode(model, source, figureProvider >+ .getFigure(dest)); >+ } else { >+ createNode(model, source); >+ } >+ } >+ continue; >+ } >+ // If any of the source, dest is null or the edge is filtered, >+ // don't create the graph. >+ if (source != null && dest != null >+ && !filterElement(getViewer().getInput(), rels[i])) { >+ createConnection(model, rels[i], getCastedContent() >+ .getSource(rels[i]), getCastedContent() >+ .getDestination(rels[i])); >+ } >+ } >+ } >+ >+ } >+ >+ private IGraphContentProvider getCastedContent() { >+ return (IGraphContentProvider) getContentProvider(); >+ } >+ >+ /* >+ * (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); >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see >+ * org.eclipse.zest.core.internal.graphmodel.IStylingGraphModelFactory#refresh >+ * (org.eclipse.zest.core.internal.graphmodel.GraphModel, java.lang.Object, >+ * boolean) >+ */ >+ public void refresh(Graph graph, Object element, boolean updateLabels) { >+ GraphConnection conn = viewer.getGraphModelConnection(element); >+ if (conn == null) { >+ // did the user send us a node? Check all of the connections on the >+ // node. >+ GraphNode node = viewer.getGraphModelNode(element); >+ if (node != null) { >+ List connections = node.getSourceConnections(); >+ for (Iterator it = connections.iterator(); it.hasNext();) { >+ GraphConnection c = (GraphConnection) it.next(); >+ refresh(graph, c.getExternalConnection(), updateLabels); >+ } >+ connections = node.getTargetConnections(); >+ for (Iterator it = connections.iterator(); it.hasNext();) { >+ GraphConnection c = (GraphConnection) it.next(); >+ refresh(graph, c.getExternalConnection(), updateLabels); >+ } >+ } >+ return; >+ } >+ Object oldSource = conn.getSource().getData(); >+ Object oldDest = conn.getDestination().getData(); >+ Object newSource = getCastedContent().getSource(element); >+ Object newDest = getCastedContent().getDestination(element); >+ if (!(oldSource.equals(newSource) && oldDest.equals(newDest))) { >+ GraphNode internalSource = viewer.getGraphModelNode(newSource); >+ GraphNode internalDest = viewer.getGraphModelNode(newDest); >+ if (internalSource == null) { >+ internalSource = createNode(graph, newSource); >+ } else if (updateLabels) { >+ styleItem(internalSource); >+ } >+ if (internalDest == null) { >+ internalDest = createNode(graph, newDest); >+ } else if (updateLabels) { >+ styleItem(internalDest); >+ } >+ if (updateLabels) { >+ styleItem(conn); >+ } >+ } >+ >+ } >+ >+} >Index: src/org/eclipse/zest/core/viewers/internal/IStylingGraphModelFactory.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/viewers/internal/IStylingGraphModelFactory.java,v >retrieving revision 1.6 >diff -u -r1.6 IStylingGraphModelFactory.java >--- src/org/eclipse/zest/core/viewers/internal/IStylingGraphModelFactory.java 12 Sep 2007 20:44:39 -0000 1.6 >+++ src/org/eclipse/zest/core/viewers/internal/IStylingGraphModelFactory.java 15 Mar 2010 03:48:56 -0000 >@@ -1,171 +1,172 @@ >-/******************************************************************************* >- * Copyright 2005-2006, 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 org.eclipse.jface.viewers.IBaseLabelProvider; >-import org.eclipse.jface.viewers.IStructuredContentProvider; >-import org.eclipse.jface.viewers.StructuredViewer; >-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; >- >-/** >- * A Graph model factory that supports the structural and visual refreshing of >- * graph elements based on the content provider and label provider in the viewer >- * that this factory is associated with. Model elements are created using the >- * content provider supplied by getContentProvider(), and styled using the label >- * provider supplied by getLabelProvider(). By the end of creation and >- * refreshing, the graph model elements are expected to be styled according to >- * the given label provider, however, default styles are dependant on the >- * particular implementation of IStylingGraphModelFactory. Unless otherwise >- * documented, clients should expect that the implementation of >- * IStylingGraphModelFactory adheres to the general defaults found in >- * {@link IZestGraphDefaults}. >- * >- * @author Del Myers >- */ >- >-public interface IStylingGraphModelFactory { >- /** >- * Returns the label provider used in this factory. >- * >- * @return the label provider used in this factory. >- */ >- public IBaseLabelProvider getLabelProvider(); >- >- /** >- * Returns the content provider used in this factory. >- * >- * @return the content provider used in this factory. >- */ >- public IStructuredContentProvider getContentProvider(); >- >- /** >- * Creates and returns the graph model from this factory based on the label >- * provider and the label provider returned in getContentProvider() and >- * getLabelProvider(). >- * >- * @return the created graph model. >- */ >- public Graph createGraphModel(Graph model); >- >- /** >- * Creates and returns a node on the given graph based on the user model >- * data, "data", using the content provider returned by >- * getContentProvider(). They node will also be styled according to the >- * information given by the label provider. If the node already exists in >- * the graph, it is restyled and returned; no new node is created. >- * >- * @param graph >- * the graph to create or retrieve the node on. >- * @param element >- * the user model data to use in the node. >- * @return the node created or retrieved for the given graph. >- */ >- public GraphNode createNode(Graph graph, Object element); >- >- /** >- * Creates and returns a connection with the given source and destination >- * objects from the user model. If the source and destination nodes don't >- * exist for the given user model objects, they are created using >- * createNode(GraphModel, Object). If a connection already exists for the >- * given user data, but with different source or destinations, it is >- * disconnected and reconnected to the given source and destination. It is >- * always styled according to the label provider provided by >- * getLabelProvider(). >- * >- * @param graph >- * the graph to create or retrieve the connection on. >- * @param element >- * the user model data to use in this connection. >- * @param source >- * the user model data used for the source node. >- * @param dest >- * the user model data used for the destination node. >- * @return the created or retrieved connection for the given graph. >- */ >- public GraphConnection createConnection(Graph graph, Object element, Object source, Object dest); >- >- /** >- * Restyles the given graph items according to the label provider supplied >- * by getLabelProvider(). >- * >- * @param items >- * the items to update. >- */ >- public void update(GraphItem[] items); >- >- /** >- * Restyles the given graph item according to the label provider supplied by >- * getLabelProvider(). >- * >- * @param item >- * the item to update. >- */ >- public void update(GraphItem item); >- >- /** >- * Structurally refreshes the graph model nodes and connections associated >- * with the given user model element. Does nothing if the element does not >- * currently exist in the view. No restyling is done by default. >- * >- * @param graph >- * @param element >- * the element to restructure. >- */ >- public void refresh(Graph graph, Object element); >- >- /** >- * Structurally refreshes the graph model nodes and connections associated >- * with the given user model element. If updateLabels is true, then the >- * labels are updated as well. Does nothing if the element does not >- * currently exist in the view. >- * >- * @param graph >- * the graph to find the element on. >- * @param element >- * the user model element. >- * @param updateLabels >- * true if the labels should be updated as well. >- */ >- public void refresh(Graph graph, Object element, boolean updateLabels); >- >- /** >- * Structurally refreshes the entire graph. >- * >- * @param graph >- * the graph to refresh; >- */ >- public void refreshGraph(Graph graph); >- >- /** >- * Returns the viewer that this factory is building the model for. >- * >- * @return the viewer that this factory is building the model for. >- */ >- public StructuredViewer getViewer(); >- >- public void setConnectionStyle(int style); >- >- /** >- * @return the connectionStyle >- */ >- public int getConnectionStyle(); >- >- public void setNodeStyle(int style); >- >- /** >- * @return the nodeStyle >- */ >- public int getNodeStyle(); >- >-} >+/******************************************************************************* >+ * Copyright 2005-2010, 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 org.eclipse.jface.viewers.IBaseLabelProvider; >+import org.eclipse.jface.viewers.IStructuredContentProvider; >+import org.eclipse.jface.viewers.StructuredViewer; >+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; >+ >+/** >+ * A Graph model factory that supports the structural and visual refreshing of >+ * graph elements based on the content provider and label provider in the viewer >+ * that this factory is associated with. Model elements are created using the >+ * content provider supplied by getContentProvider(), and styled using the label >+ * provider supplied by getLabelProvider(). By the end of creation and >+ * refreshing, the graph model elements are expected to be styled according to >+ * the given label provider, however, default styles are dependant on the >+ * particular implementation of IStylingGraphModelFactory. Unless otherwise >+ * documented, clients should expect that the implementation of >+ * IStylingGraphModelFactory adheres to the general defaults found in >+ * {@link IZestGraphDefaults}. >+ * >+ * @author Del Myers >+ */ >+ >+public interface IStylingGraphModelFactory { >+ /** >+ * Returns the label provider used in this factory. >+ * >+ * @return the label provider used in this factory. >+ */ >+ public IBaseLabelProvider getLabelProvider(); >+ >+ /** >+ * Returns the content provider used in this factory. >+ * >+ * @return the content provider used in this factory. >+ */ >+ public IStructuredContentProvider getContentProvider(); >+ >+ /** >+ * Creates and returns the graph model from this factory based on the label >+ * provider and the label provider returned in getContentProvider() and >+ * getLabelProvider(). >+ * >+ * @return the created graph model. >+ */ >+ public Graph createGraphModel(Graph model); >+ >+ /** >+ * Creates and returns a node on the given graph based on the user model >+ * data, "data", using the content provider returned by >+ * getContentProvider(). They node will also be styled according to the >+ * information given by the label provider. If the node already exists in >+ * the graph, it is restyled and returned; no new node is created. >+ * >+ * @param graph >+ * the graph to create or retrieve the node on. >+ * @param element >+ * the user model data to use in the node. >+ * @return the node created or retrieved for the given graph. >+ */ >+ public GraphNode createNode(Graph graph, Object element); >+ >+ /** >+ * Creates and returns a connection with the given source and destination >+ * objects from the user model. If the source and destination nodes don't >+ * exist for the given user model objects, they are created using >+ * createNode(GraphModel, Object). If a connection already exists for the >+ * given user data, but with different source or destinations, it is >+ * disconnected and reconnected to the given source and destination. It is >+ * always styled according to the label provider provided by >+ * getLabelProvider(). >+ * >+ * @param graph >+ * the graph to create or retrieve the connection on. >+ * @param element >+ * the user model data to use in this connection. >+ * @param source >+ * the user model data used for the source node. >+ * @param dest >+ * the user model data used for the destination node. >+ * @return the created or retrieved connection for the given graph. >+ */ >+ public GraphConnection createConnection(Graph graph, Object element, >+ Object source, Object dest); >+ >+ /** >+ * Restyles the given graph items according to the label provider supplied >+ * by getLabelProvider(). >+ * >+ * @param items >+ * the items to update. >+ */ >+ public void update(GraphItem[] items); >+ >+ /** >+ * Restyles the given graph item according to the label provider supplied by >+ * getLabelProvider(). >+ * >+ * @param item >+ * the item to update. >+ */ >+ public void update(GraphItem item); >+ >+ /** >+ * Structurally refreshes the graph model nodes and connections associated >+ * with the given user model element. Does nothing if the element does not >+ * currently exist in the view. No restyling is done by default. >+ * >+ * @param graph >+ * @param element >+ * the element to restructure. >+ */ >+ public void refresh(Graph graph, Object element); >+ >+ /** >+ * Structurally refreshes the graph model nodes and connections associated >+ * with the given user model element. If updateLabels is true, then the >+ * labels are updated as well. Does nothing if the element does not >+ * currently exist in the view. >+ * >+ * @param graph >+ * the graph to find the element on. >+ * @param element >+ * the user model element. >+ * @param updateLabels >+ * true if the labels should be updated as well. >+ */ >+ public void refresh(Graph graph, Object element, boolean updateLabels); >+ >+ /** >+ * Structurally refreshes the entire graph. >+ * >+ * @param graph >+ * the graph to refresh; >+ */ >+ public void refreshGraph(Graph graph); >+ >+ /** >+ * Returns the viewer that this factory is building the model for. >+ * >+ * @return the viewer that this factory is building the model for. >+ */ >+ public StructuredViewer getViewer(); >+ >+ public void setConnectionStyle(int style); >+ >+ /** >+ * @return the connectionStyle >+ */ >+ public int getConnectionStyle(); >+ >+ public void setNodeStyle(int style); >+ >+ /** >+ * @return the nodeStyle >+ */ >+ public int getNodeStyle(); >+ >+} >Index: src/org/eclipse/zest/core/viewers/internal/SharedMessages.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/viewers/internal/SharedMessages.java,v >retrieving revision 1.3 >diff -u -r1.3 SharedMessages.java >--- src/org/eclipse/zest/core/viewers/internal/SharedMessages.java 12 Sep 2007 20:44:39 -0000 1.3 >+++ src/org/eclipse/zest/core/viewers/internal/SharedMessages.java 15 Mar 2010 03:48:56 -0000 >@@ -1,32 +1,32 @@ >-/******************************************************************************* >- * Copyright (c) 2003, 2005 IBM Corporation and others. 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: IBM Corporation - initial API and implementation >- ******************************************************************************/ >-package org.eclipse.zest.core.viewers.internal; >- >-/** >- * This class contains UI strings (translated, if available) that clients can >- * use. >- * >- * @author Eric Bordeau >- */ >-public class SharedMessages { >- >- /** >- * The string "Page". >- */ >- public static String FitAllAction_Label = "Page"; //GEFMessages.FitAllAction_Label; >- /** >- * The string "Width". >- */ >- public static String FitWidthAction_Label = "Width"; //GEFMessages.FitWidthAction_Label; >- /** >- * The string "Height". >- */ >- public static String FitHeightAction_Label = "Height"; //GEFMessages.FitHeightAction_Label; >- >-} >+/******************************************************************************* >+ * Copyright (c) 2003, 2005-2010 IBM Corporation and others. 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: IBM Corporation - initial API and implementation >+ ******************************************************************************/ >+package org.eclipse.zest.core.viewers.internal; >+ >+/** >+ * This class contains UI strings (translated, if available) that clients can >+ * use. >+ * >+ * @author Eric Bordeau >+ */ >+public class SharedMessages { >+ >+ /** >+ * The string "Page". >+ */ >+ public static String FitAllAction_Label = "Page"; // GEFMessages.FitAllAction_Label; >+ /** >+ * The string "Width". >+ */ >+ public static String FitWidthAction_Label = "Width"; // GEFMessages.FitWidthAction_Label; >+ /** >+ * The string "Height". >+ */ >+ public static String FitHeightAction_Label = "Height"; // GEFMessages.FitHeightAction_Label; >+ >+} >Index: src/org/eclipse/zest/core/widgets/DAGExpandCollapseManager.java >=================================================================== >RCS file: src/org/eclipse/zest/core/widgets/DAGExpandCollapseManager.java >diff -N src/org/eclipse/zest/core/widgets/DAGExpandCollapseManager.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/zest/core/widgets/DAGExpandCollapseManager.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,300 @@ >+/******************************************************************************* >+ * Copyright (c) 2009-2010 Mateusz Matela and others. 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: Mateusz Matela - initial API and implementation >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.core.widgets; >+ >+import java.util.HashSet; >+import java.util.Iterator; >+ >+import org.eclipse.zest.layouts.interfaces.ConnectionLayout; >+import org.eclipse.zest.layouts.interfaces.ContextListener; >+import org.eclipse.zest.layouts.interfaces.ExpandCollapseManager; >+import org.eclipse.zest.layouts.interfaces.GraphStructureListener; >+import org.eclipse.zest.layouts.interfaces.LayoutContext; >+import org.eclipse.zest.layouts.interfaces.NodeLayout; >+ >+/** >+ * <p> >+ * An {@link ExpandCollapseManager} specialized for Directed Acyclic Graphs. It >+ * works correctly only when all connections are directed (and of course nodes >+ * form an acyclic graph). It's supposed to be used with >+ * {@link InternalLayoutContext}. >+ * </p> >+ * <p> >+ * When a node is collapsed, all its outgoing connections are hidden and these >+ * successors that have no visible incoming nodes are pruned. When a node is >+ * expanded, all its successors are unpruned and connections pointing to them >+ * are shown. >+ * </p> >+ * </p> >+ * <p> >+ * <b>NOTE:</b> A <code>Graph</code> using this manger should use >+ * {@link DefaultSubgraph}, which doesn't show any information about subgraphs >+ * in the graph. That's because for this manager it doesn't matter which >+ * subgraph a node belongs to (each pruning creates a new subgraph). Also, this >+ * manager adds a label to each collapsed node showing number of its successors. >+ * </p> >+ * One instance of this class can serve only one instance of <code>Graph</code>. >+ * >+ * @since 2.0 >+ */ >+public class DAGExpandCollapseManager implements ExpandCollapseManager { >+ >+ private InternalLayoutContext context; >+ >+ private HashSet expandedNodes = new HashSet(); >+ >+ private HashSet nodesToPrune = new HashSet(); >+ >+ private HashSet nodesToUnprune = new HashSet(); >+ >+ private HashSet nodesToUpdate = new HashSet(); >+ >+ private boolean cleanLayoutScheduled = false; >+ >+ public void initExpansion(final LayoutContext context2) { >+ if (!(context2 instanceof InternalLayoutContext)) { >+ throw new RuntimeException( >+ "This manager works only with org.eclipse.zest.core.widgets.InternalLayoutContext"); >+ } >+ context = (InternalLayoutContext) context2; >+ >+ context.addGraphStructureListener(new GraphStructureListener() { >+ public boolean nodeRemoved(LayoutContext context, NodeLayout node) { >+ if (isExpanded(node)) { >+ collapse(node); >+ } >+ flushChanges(false, true); >+ return false; >+ } >+ >+ public boolean nodeAdded(LayoutContext context, NodeLayout node) { >+ resetState(node); >+ flushChanges(false, true); >+ return false; >+ } >+ >+ public boolean connectionRemoved(LayoutContext context, >+ ConnectionLayout connection) { >+ NodeLayout target = connection.getTarget(); >+ if (!isExpanded(target) >+ && target.getIncomingConnections().length == 0) { >+ expand(target); >+ } >+ flushChanges(false, true); >+ return false; >+ } >+ >+ public boolean connectionAdded(LayoutContext context, >+ ConnectionLayout connection) { >+ resetState(connection.getTarget()); >+ updateNodeLabel(connection.getSource()); >+ flushChanges(false, true); >+ return false; >+ } >+ >+ }); >+ >+ context.addContextListener(new ContextListener.Stub() { >+ public void backgroundEnableChanged(LayoutContext context) { >+ flushChanges(false, false); >+ } >+ }); >+ } >+ >+ public boolean canCollapse(LayoutContext context, NodeLayout node) { >+ return isExpanded(node) && !node.isPruned() >+ && node.getOutgoingConnections().length > 0; >+ } >+ >+ public boolean canExpand(LayoutContext context, NodeLayout node) { >+ return !isExpanded(node) && !node.isPruned() >+ && node.getOutgoingConnections().length > 0; >+ } >+ >+ private void collapseAllConnections(NodeLayout node) { >+ ConnectionLayout[] outgoingConnections = node.getOutgoingConnections(); >+ for (int i = 0; i < outgoingConnections.length; i++) { >+ outgoingConnections[i].setVisible(false); >+ } >+ flushChanges(true, true); >+ } >+ >+ private void expandAllConnections(NodeLayout node) { >+ ConnectionLayout[] outgoingConnections = node.getOutgoingConnections(); >+ for (int i = 0; i < outgoingConnections.length; i++) { >+ outgoingConnections[i].setVisible(true); >+ } >+ flushChanges(true, true); >+ } >+ >+ public void setExpanded(LayoutContext context, NodeLayout node, >+ boolean expanded) { >+ >+ // if (isExpanded(node) == expanded) >+ // return; >+ if (expanded) { >+ if (canExpand(context, node)) { >+ expand(node); >+ } >+ expandAllConnections(node); >+ } else { >+ if (canCollapse(context, node)) { >+ collapse(node); >+ } >+ collapseAllConnections(node); >+ } >+ flushChanges(true, true); >+ } >+ >+ private void expand(NodeLayout node) { >+ setExpanded(node, true); >+ NodeLayout[] successingNodes = node.getSuccessingNodes(); >+ for (int i = 0; i < successingNodes.length; i++) { >+ unpruneNode(successingNodes[i]); >+ } >+ updateNodeLabel(node); >+ } >+ >+ private void collapse(NodeLayout node) { >+ if (isExpanded(node)) { >+ setExpanded(node, false); >+ } else { >+ return; >+ } >+ NodeLayout[] successors = node.getSuccessingNodes(); >+ for (int i = 0; i < successors.length; i++) { >+ checkPruning(successors[i]); >+ if (isPruned(successors[i])) { >+ collapse(successors[i]); >+ } >+ } >+ updateNodeLabel(node); >+ } >+ >+ private void checkPruning(NodeLayout node) { >+ boolean prune = true; >+ NodeLayout[] predecessors = node.getPredecessingNodes(); >+ for (int j = 0; j < predecessors.length; j++) { >+ if (isExpanded(predecessors[j])) { >+ prune = false; >+ break; >+ } >+ } >+ if (prune) { >+ pruneNode(node); >+ } else { >+ unpruneNode(node); >+ } >+ } >+ >+ /** >+ * By default nodes at the top (having no predecessors) are expanded. The >+ * rest are collapsed and pruned if they don't have any expanded >+ * predecessors >+ * >+ * @param target >+ */ >+ private void resetState(NodeLayout node) { >+ NodeLayout[] predecessors = node.getPredecessingNodes(); >+ if (predecessors.length == 0) { >+ expand(node); >+ } else { >+ collapse(node); >+ checkPruning(node); >+ } >+ } >+ >+ /** >+ * If given node belongs to a layout context using >+ * {@link PrunedSuccessorsSubgraph}, update of the nodes's label is forced. >+ * >+ * @param node >+ * node to update >+ */ >+ private void updateNodeLabel(NodeLayout node) { >+ nodesToUpdate.add(node); >+ } >+ >+ private void updateNodeLabel2(InternalNodeLayout node) { >+ SubgraphFactory subgraphFactory = node.getOwnerLayoutContext() >+ .getSubgraphFactory(); >+ if (subgraphFactory instanceof DefaultSubgraph.PrunedSuccessorsSubgraphFactory) { >+ ((DefaultSubgraph.PrunedSuccessorsSubgraphFactory) subgraphFactory) >+ .updateLabelForNode(node); >+ } >+ } >+ >+ private void pruneNode(NodeLayout node) { >+ if (isPruned(node)) { >+ return; >+ } >+ nodesToUnprune.remove(node); >+ nodesToPrune.add(node); >+ } >+ >+ private void unpruneNode(NodeLayout node) { >+ if (!isPruned(node)) { >+ return; >+ } >+ nodesToPrune.remove(node); >+ nodesToUnprune.add(node); >+ } >+ >+ private boolean isPruned(NodeLayout node) { >+ if (nodesToUnprune.contains(node)) { >+ return false; >+ } >+ if (nodesToPrune.contains(node)) { >+ return true; >+ } >+ return node.isPruned(); >+ } >+ >+ private void flushChanges(boolean force, boolean clean) { >+ cleanLayoutScheduled = cleanLayoutScheduled || clean; >+ if (!force && !context.isBackgroundLayoutEnabled()) { >+ return; >+ } >+ >+ for (Iterator iterator = nodesToUnprune.iterator(); iterator.hasNext();) { >+ NodeLayout node = (NodeLayout) iterator.next(); >+ node.prune(null); >+ } >+ nodesToUnprune.clear(); >+ >+ if (!nodesToPrune.isEmpty()) { >+ context.createSubgraph((NodeLayout[]) nodesToPrune >+ .toArray(new NodeLayout[nodesToPrune.size()])); >+ nodesToPrune.clear(); >+ } >+ >+ for (Iterator iterator = nodesToUpdate.iterator(); iterator.hasNext();) { >+ InternalNodeLayout node = (InternalNodeLayout) iterator.next(); >+ updateNodeLabel2(node); >+ } >+ nodesToUpdate.clear(); >+ >+ (context).applyLayout(cleanLayoutScheduled); >+ cleanLayoutScheduled = false; >+ context.flushChanges(true); >+ } >+ >+ private boolean isExpanded(NodeLayout node) { >+ return expandedNodes.contains(node); >+ } >+ >+ private void setExpanded(NodeLayout node, boolean expanded) { >+ if (expanded) { >+ expandedNodes.add(node); >+ } else { >+ expandedNodes.remove(node); >+ } >+ } >+} >Index: src/org/eclipse/zest/core/widgets/DefaultSubgraph.java >=================================================================== >RCS file: src/org/eclipse/zest/core/widgets/DefaultSubgraph.java >diff -N src/org/eclipse/zest/core/widgets/DefaultSubgraph.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/zest/core/widgets/DefaultSubgraph.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,380 @@ >+/******************************************************************************* >+ * Copyright (c) 2009-2010 Mateusz Matela and others. 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: Mateusz Matela - initial API and implementation >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.core.widgets; >+ >+import java.util.HashMap; >+import java.util.HashSet; >+import java.util.Iterator; >+import java.util.Set; >+ >+import org.eclipse.draw2d.ColorConstants; >+import org.eclipse.swt.graphics.Color; >+import org.eclipse.zest.core.widgets.custom.LabelSubgraph; >+import org.eclipse.zest.core.widgets.custom.TriangleSubgraph; >+import org.eclipse.zest.core.widgets.custom.TriangleSubgraph.TriangleParameters; >+import org.eclipse.zest.layouts.dataStructures.DisplayIndependentDimension; >+import org.eclipse.zest.layouts.dataStructures.DisplayIndependentPoint; >+import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle; >+import org.eclipse.zest.layouts.interfaces.ConnectionLayout; >+import org.eclipse.zest.layouts.interfaces.EntityLayout; >+import org.eclipse.zest.layouts.interfaces.LayoutContext; >+import org.eclipse.zest.layouts.interfaces.NodeLayout; >+import org.eclipse.zest.layouts.interfaces.SubgraphLayout; >+ >+/** >+ * Default implementation of {@link SubgraphLayout}. Every subgraph added to >+ * Zest {@link Graph} should inherit from this class. The default implementation >+ * is very simple. A node pruned to this subgraph is minimized and all >+ * connections adjacent to it are made invisible. No additional graphic elements >+ * are added to the graph, but subclasses may add them. >+ * >+ * @since 2.0 >+ */ >+public class DefaultSubgraph implements SubgraphLayout { >+ >+ /** >+ * Default factory for {@link DefaultSubgraph}. It creates one subgraph for >+ * a whole graph and throws every node intimageo it. >+ */ >+ public static class DefaultSubgraphFactory implements SubgraphFactory { >+ private HashMap contextToSubgraph = new HashMap(); >+ >+ public SubgraphLayout createSubgraph(NodeLayout[] nodes, >+ LayoutContext context) { >+ DefaultSubgraph subgraph = (DefaultSubgraph) contextToSubgraph >+ .get(context); >+ if (subgraph == null) { >+ subgraph = new DefaultSubgraph(context); >+ contextToSubgraph.put(context, subgraph); >+ } >+ subgraph.addNodes(nodes); >+ return subgraph; >+ } >+ }; >+ >+ public static class LabelSubgraphFactory implements SubgraphFactory { >+ private Color defaultForegroundColor = ColorConstants.black; >+ private Color defaultBackgroundColor = ColorConstants.yellow; >+ >+ /** >+ * Changes the default foreground color for newly created subgraphs. >+ * >+ * @param c >+ * color to use >+ */ >+ public void setDefualtForegroundColor(Color c) { >+ defaultForegroundColor = c; >+ } >+ >+ /** >+ * Changes the default background color for newly created subgraphs. >+ * >+ * @param c >+ * color to use >+ */ >+ public void setDefaultBackgroundColor(Color c) { >+ defaultBackgroundColor = c; >+ } >+ >+ public SubgraphLayout createSubgraph(NodeLayout[] nodes, >+ LayoutContext context) { >+ return new LabelSubgraph(nodes, context, defaultForegroundColor, >+ defaultBackgroundColor); >+ } >+ }; >+ >+ public static class TriangleSubgraphFactory implements SubgraphFactory { >+ private TriangleParameters parameters = new TriangleParameters(); >+ >+ public SubgraphLayout createSubgraph(NodeLayout[] nodes, >+ LayoutContext context) { >+ return new TriangleSubgraph(nodes, context, >+ (TriangleParameters) parameters.clone()); >+ } >+ >+ /** >+ * >+ * @return initial color of triangles created with this factory >+ */ >+ public Color getColor() { >+ return parameters.color; >+ } >+ >+ /** >+ * Changes the default color for newly created subgraphs. >+ * >+ * @param color >+ * color to use >+ */ >+ public void setColor(Color color) { >+ parameters.color = color; >+ } >+ >+ /** >+ * >+ * @return initial direction of triangles created with this factory >+ */ >+ public int getDirection() { >+ return parameters.direction; >+ } >+ >+ /** >+ * Changes the default direction for newly cretaed subgraphs. >+ * >+ * @param direction >+ * direction to use, can be {@link SubgraphLayout#TOP_DOWN}, >+ * {@link SubgraphLayout#BOTTOM_UP}, >+ * {@link SubgraphLayout#LEFT_RIGHT}, or >+ * {@link SubgraphLayout#RIGHT_LEFT} >+ */ >+ public void setDirection(int direction) { >+ parameters.direction = direction; >+ } >+ >+ /** >+ * >+ * @return maximum height of triangles created with this factory >+ */ >+ public double getReferenceHeight() { >+ return parameters.referenceHeight; >+ } >+ >+ /** >+ * Sets the maximum height for the triangle visualizing this subgraph. >+ * >+ * @param referenceHeight >+ * height to use >+ */ >+ public void setReferenceHeight(double referenceHeight) { >+ parameters.referenceHeight = referenceHeight; >+ } >+ >+ /** >+ * >+ * @return maximum base length of triangles created with this factory >+ */ >+ public double getReferenceBase() { >+ return parameters.referenceBase; >+ } >+ >+ /** >+ * Sets the maximum base length for the triangle visualizing this >+ * subgraph. >+ * >+ * @param referenceBase >+ * base length to use >+ */ >+ >+ public void setReferenceBase(double referenceBase) { >+ parameters.referenceBase = referenceBase; >+ } >+ }; >+ >+ /** >+ * Factory for {@link PrunedSuccessorsSubgraph}. It creates one subgraph for >+ * a whole graph and throws every node into it. >+ */ >+ public static class PrunedSuccessorsSubgraphFactory implements >+ SubgraphFactory { >+ private HashMap contextToSubgraph = new HashMap(); >+ >+ public SubgraphLayout createSubgraph(NodeLayout[] nodes, >+ LayoutContext context) { >+ PrunedSuccessorsSubgraph subgraph = (PrunedSuccessorsSubgraph) contextToSubgraph >+ .get(context); >+ if (subgraph == null) { >+ subgraph = new PrunedSuccessorsSubgraph(context); >+ contextToSubgraph.put(context, subgraph); >+ } >+ subgraph.addNodes(nodes); >+ return subgraph; >+ } >+ >+ /** >+ * Updates a label for given node (creates the label if necessary). >+ * >+ * @param node >+ * node to update >+ */ >+ public void updateLabelForNode(InternalNodeLayout node) { >+ InternalLayoutContext context = node.getOwnerLayoutContext(); >+ PrunedSuccessorsSubgraph subgraph = (PrunedSuccessorsSubgraph) contextToSubgraph >+ .get(context); >+ if (subgraph == null) { >+ subgraph = new PrunedSuccessorsSubgraph(context); >+ contextToSubgraph.put(context, subgraph); >+ } >+ subgraph.updateNodeLabel(node); >+ } >+ >+ }; >+ >+ protected final InternalLayoutContext context; >+ >+ protected final Set nodes = new HashSet(); >+ >+ protected boolean disposed = false; >+ >+ protected DefaultSubgraph(LayoutContext context2) { >+ if (context2 instanceof InternalLayoutContext) { >+ this.context = (InternalLayoutContext) context2; >+ } else { >+ throw new RuntimeException( >+ "This subgraph can be only created with LayoutContext provided by Zest Graph"); >+ } >+ } >+ >+ public boolean isGraphEntity() { >+ return false; >+ } >+ >+ public void setSize(double width, double height) { >+ // do nothing >+ context.checkChangesAllowed(); >+ } >+ >+ public void setLocation(double x, double y) { >+ // do nothing >+ context.checkChangesAllowed(); >+ } >+ >+ public boolean isResizable() { >+ return false; >+ } >+ >+ public boolean isMovable() { >+ return false; >+ } >+ >+ public EntityLayout[] getSuccessingEntities() { >+ return new EntityLayout[0]; >+ } >+ >+ public DisplayIndependentDimension getSize() { >+ DisplayIndependentRectangle bounds = context.getBounds(); >+ return new DisplayIndependentDimension(bounds.width, bounds.height); >+ } >+ >+ public double getPreferredAspectRatio() { >+ return 0; >+ } >+ >+ public EntityLayout[] getPredecessingEntities() { >+ return new EntityLayout[0]; >+ } >+ >+ public DisplayIndependentPoint getLocation() { >+ DisplayIndependentRectangle bounds = context.getBounds(); >+ return new DisplayIndependentPoint(bounds.x + bounds.width / 2, >+ bounds.y + bounds.height / 2); >+ } >+ >+ public boolean isDirectionDependant() { >+ return false; >+ } >+ >+ public void setDirection(int direction) { >+ context.checkChangesAllowed(); >+ // do nothing >+ } >+ >+ public void removeNodes(NodeLayout[] nodes) { >+ context.checkChangesAllowed(); >+ for (int i = 0; i < nodes.length; i++) { >+ if (this.nodes.remove(nodes[i])) { >+ nodes[i].prune(null); >+ nodes[i].setMinimized(false); >+ refreshConnectionsVisibility(nodes[i].getIncomingConnections()); >+ refreshConnectionsVisibility(nodes[i].getOutgoingConnections()); >+ } >+ } >+ if (this.nodes.isEmpty()) { >+ dispose(); >+ } >+ } >+ >+ public void removeDisposedNodes() { >+ for (Iterator iterator = nodes.iterator(); iterator.hasNext();) { >+ InternalNodeLayout node = (InternalNodeLayout) iterator.next(); >+ if (node.isDisposed()) { >+ iterator.remove(); >+ } >+ } >+ } >+ >+ public NodeLayout[] getNodes() { >+ InternalNodeLayout[] result = new InternalNodeLayout[nodes.size()]; >+ int i = 0; >+ for (Iterator iterator = nodes.iterator(); iterator.hasNext();) { >+ result[i] = (InternalNodeLayout) iterator.next(); >+ if (!context.isLayoutItemFiltered(result[i].getNode())) { >+ i++; >+ } >+ } >+ if (i == nodes.size()) { >+ return result; >+ } >+ >+ NodeLayout[] result2 = new NodeLayout[i]; >+ System.arraycopy(result, 0, result2, 0, i); >+ return result2; >+ } >+ >+ public int countNodes() { >+ return nodes.size(); >+ } >+ >+ public void addNodes(NodeLayout[] nodes) { >+ context.checkChangesAllowed(); >+ for (int i = 0; i < nodes.length; i++) { >+ if (this.nodes.add(nodes[i])) { >+ nodes[i].prune(this); >+ nodes[i].setMinimized(true); >+ refreshConnectionsVisibility(nodes[i].getIncomingConnections()); >+ refreshConnectionsVisibility(nodes[i].getOutgoingConnections()); >+ } >+ } >+ } >+ >+ protected void refreshConnectionsVisibility(ConnectionLayout[] connections) { >+ for (int i = 0; i < connections.length; i++) { >+ connections[i].setVisible(!connections[i].getSource().isPruned() >+ && !connections[i].getTarget().isPruned()); >+ } >+ } >+ >+ /** >+ * Makes sure that value returned by {@link #getLocation()} will be equal to >+ * current location of this subgraph. >+ */ >+ protected void refreshLocation() { >+ // do nothing, to be reimplemented in subclasses >+ } >+ >+ /** >+ * Makes sure that value returned by {@link #getSize()} will be equal to >+ * current size of this subgraph. >+ */ >+ protected void refreshSize() { >+ // do nothing, to be reimplemented in subclasses >+ } >+ >+ protected void applyLayoutChanges() { >+ // do nothing >+ } >+ >+ protected void dispose() { >+ if (!disposed) { >+ context.removeSubgrah(this); >+ disposed = true; >+ } >+ } >+ >+}; >\ No newline at end of file >Index: src/org/eclipse/zest/core/widgets/FigureSubgraph.java >=================================================================== >RCS file: src/org/eclipse/zest/core/widgets/FigureSubgraph.java >diff -N src/org/eclipse/zest/core/widgets/FigureSubgraph.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/zest/core/widgets/FigureSubgraph.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,198 @@ >+/******************************************************************************* >+ * Copyright (c) 2009-2010 Mateusz Matela and others. 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: Mateusz Matela - initial API and implementation >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.core.widgets; >+ >+import java.util.Iterator; >+ >+import org.eclipse.draw2d.Animation; >+import org.eclipse.draw2d.FigureListener; >+import org.eclipse.draw2d.IFigure; >+import org.eclipse.draw2d.geometry.Dimension; >+import org.eclipse.draw2d.geometry.Point; >+import org.eclipse.draw2d.geometry.Rectangle; >+import org.eclipse.zest.layouts.dataStructures.DisplayIndependentDimension; >+import org.eclipse.zest.layouts.dataStructures.DisplayIndependentPoint; >+import org.eclipse.zest.layouts.interfaces.EntityLayout; >+import org.eclipse.zest.layouts.interfaces.LayoutContext; >+import org.eclipse.zest.layouts.interfaces.NodeLayout; >+ >+/** >+ * A subgraph layout that represents a subgraph as a single figure. An entity >+ * representing subgraph is not resizable by layout algorithm unless proper >+ * methods are redefined in a subclass. >+ * >+ * @since 2.0 >+ */ >+public abstract class FigureSubgraph extends DefaultSubgraph { >+ >+ protected IFigure figure; >+ private DisplayIndependentPoint location; >+ private boolean isLayoutBeingApplied = false; >+ >+ /** >+ * Listens to changes in this subgraph's figure and fires proper event in >+ * its layout context. >+ */ >+ protected class SubgraphFigrueListener implements FigureListener { >+ private Rectangle previousBounds = figure.getBounds().getCopy(); >+ >+ public void figureMoved(IFigure source) { >+ if (Animation.isAnimating() || isLayoutBeingApplied) { >+ return; >+ } >+ Rectangle newBounds = figure.getBounds(); >+ if (!newBounds.getSize().equals(previousBounds.getSize())) { >+ (context).fireSubgraphResizedEvent(FigureSubgraph.this); >+ } else if (!newBounds.getLocation().equals( >+ previousBounds.getLocation())) { >+ (context).fireSubgraphMovedEvent(FigureSubgraph.this); >+ } >+ previousBounds = newBounds.getCopy(); >+ } >+ }; >+ >+ /** >+ * Creates a figure for this subgraph and stores it in {@link #figure}. >+ * >+ * This method may not be called right after creation of the subgraph but >+ * later when the figure is actually needed (lazy initialization). >+ */ >+ protected abstract void createFigure(); >+ >+ /** >+ * Updates the figure stored in {@link #figure} depending on current nodes >+ * contained in this subgraph. If this method creates a new instance of >+ * IFigure, it should remember to add a {@link SubgraphFigrueListener} to >+ * it. >+ */ >+ protected abstract void updateFigure(); >+ >+ public IFigure getFigure() { >+ if (figure == null) { >+ createFigure(); >+ updateFigure(); >+ figure.addFigureListener(new SubgraphFigrueListener()); >+ (context).container.addSubgraphFigure(figure); >+ } >+ return figure; >+ } >+ >+ protected FigureSubgraph(NodeLayout[] nodes, LayoutContext context) { >+ super(context); >+ addNodes(nodes); >+ } >+ >+ /** >+ * {@inheritDoc} >+ * >+ * All nodes added to this subgraph are moved to the center of the figure >+ * (so that collapsing and expanding animation looks cool). >+ */ >+ public void addNodes(NodeLayout[] nodes) { >+ int initialCount = this.nodes.size(); >+ super.addNodes(nodes); >+ if (this.nodes.size() > initialCount && figure != null) { >+ updateFigure(); >+ if (location != null) { >+ for (int i = 0; i < nodes.length; i++) { >+ nodes[i].setLocation(location.x, location.y); >+ } >+ } >+ } >+ } >+ >+ public void removeNodes(NodeLayout[] nodes) { >+ int initialCount = this.nodes.size(); >+ super.removeNodes(nodes); >+ if (this.nodes.size() < initialCount && figure != null && !disposed) { >+ updateFigure(); >+ } >+ } >+ >+ public EntityLayout[] getSuccessingEntities() { >+ // TODO Auto-generated method stub >+ return super.getSuccessingEntities(); >+ } >+ >+ public EntityLayout[] getPredecessingEntities() { >+ // TODO Auto-generated method stub >+ return super.getPredecessingEntities(); >+ } >+ >+ public DisplayIndependentDimension getSize() { >+ Dimension size = getFigure().getSize(); >+ return new DisplayIndependentDimension(size.width, size.height); >+ } >+ >+ public DisplayIndependentPoint getLocation() { >+ if (location == null) { >+ Point location2 = getFigure().getBounds().getLocation(); >+ Dimension size = getFigure().getSize(); >+ return new DisplayIndependentPoint(location2.x + size.width / 2, >+ location2.y + size.height / 2); >+ } >+ return new DisplayIndependentPoint(location); >+ } >+ >+ public void setLocation(double x, double y) { >+ super.setLocation(x, y); >+ for (Iterator iterator = nodes.iterator(); iterator.hasNext();) { >+ NodeLayout node = (NodeLayout) iterator.next(); >+ node.setLocation(x, y); >+ } >+ >+ if (location != null) { >+ location.x = x; >+ location.y = y; >+ } else { >+ location = new DisplayIndependentPoint(x, y); >+ // the first location change will be applied immediately >+ applyLayoutChanges(); >+ } >+ } >+ >+ protected void refreshLocation() { >+ Rectangle bounds = figure.getBounds(); >+ if (location == null) { >+ location = new DisplayIndependentPoint(0, 0); >+ } >+ location.x = bounds.x + bounds.width / 2; >+ location.y = bounds.y + bounds.height / 2; >+ } >+ >+ public boolean isGraphEntity() { >+ return true; >+ } >+ >+ public boolean isMovable() { >+ return true; >+ } >+ >+ protected void dispose() { >+ if (!disposed) { >+ super.dispose(); >+ if (figure != null) { >+ context.container.getGraph().removeSubgraphFigure(figure); >+ } >+ } >+ } >+ >+ protected void applyLayoutChanges() { >+ getFigure(); >+ if (location != null) { >+ isLayoutBeingApplied = true; >+ Dimension size = figure.getSize(); >+ figure.setLocation(new Point(location.x - size.width / 2, >+ location.y - size.height / 2)); >+ isLayoutBeingApplied = false; >+ } >+ } >+ >+} >Index: src/org/eclipse/zest/core/widgets/FisheyeListener.java >=================================================================== >RCS file: src/org/eclipse/zest/core/widgets/FisheyeListener.java >diff -N src/org/eclipse/zest/core/widgets/FisheyeListener.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/zest/core/widgets/FisheyeListener.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,61 @@ >+/******************************************************************************* >+ * Copyright (c) 2009-2010 Mateusz Matela and others. 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: Mateusz Matela - initial API and implementation >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.core.widgets; >+ >+import org.eclipse.draw2d.IFigure; >+ >+/** >+ * Interface for listener that can be added to {@link Graph} and receive >+ * notifications when fisheye figures are added, removed or replaced in it. >+ * >+ * @since 2.0 >+ */ >+public interface FisheyeListener { >+ >+ /** >+ * Called when a fisheye figure is added to an observed graph. >+ * >+ * @param graph >+ * observed graph >+ * @param originalFigure >+ * figure to be fisheyed >+ * @param fisheyeFigure >+ * the added fisheye figure >+ */ >+ public void fisheyeAdded(Graph graph, IFigure originalFigure, >+ IFigure fisheyeFigure); >+ >+ /** >+ * Called when a fisheye figure is removed form an observed graph. >+ * >+ * @param graph >+ * observed graph >+ * @param originalFigure >+ * figure that was fisheyed >+ * @param fisheyeFigure >+ * the removed fisheye figure >+ */ >+ public void fisheyeRemoved(Graph graph, IFigure originalFigure, >+ IFigure fisheyeFigure); >+ >+ /** >+ * Called when one fisheye figure is replaced by another in an observed >+ * graph. >+ * >+ * @param graph >+ * observed graph >+ * @param oldFisheyeFigure >+ * fisheye figure that is replaced >+ * @param newFisheyeFigure >+ * fisheye figure that replaces the old figure >+ */ >+ public void fisheyeReplaced(Graph graph, IFigure oldFisheyeFigure, >+ IFigure newFisheyeFigure); >+} >Index: src/org/eclipse/zest/core/widgets/Graph.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/widgets/Graph.java,v >retrieving revision 1.41 >diff -u -r1.41 Graph.java >--- src/org/eclipse/zest/core/widgets/Graph.java 30 Jan 2009 01:14:33 -0000 1.41 >+++ src/org/eclipse/zest/core/widgets/Graph.java 15 Mar 2010 03:48:58 -0000 >@@ -1,1198 +1,1217 @@ >-/******************************************************************************* >- * Copyright 2005, 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.widgets; >- >-import java.util.ArrayList; >-import java.util.HashMap; >-import java.util.Iterator; >-import java.util.LinkedList; >-import java.util.List; >- >-import org.eclipse.draw2d.Animation; >-import org.eclipse.draw2d.ColorConstants; >-import org.eclipse.draw2d.FigureCanvas; >-import org.eclipse.draw2d.FreeformLayer; >-import org.eclipse.draw2d.FreeformLayout; >-import org.eclipse.draw2d.FreeformViewport; >-import org.eclipse.draw2d.IFigure; >-import org.eclipse.draw2d.LayoutAnimator; >-import org.eclipse.draw2d.MouseMotionListener; >-import org.eclipse.draw2d.PolylineConnection; >-import org.eclipse.draw2d.SWTEventDispatcher; >-import org.eclipse.draw2d.ScalableFigure; >-import org.eclipse.draw2d.ScalableFreeformLayeredPane; >-import org.eclipse.draw2d.ScrollPane; >-import org.eclipse.draw2d.TreeSearch; >-import org.eclipse.draw2d.geometry.Dimension; >-import org.eclipse.draw2d.geometry.Point; >-import org.eclipse.draw2d.geometry.Rectangle; >-import org.eclipse.swt.SWT; >-import org.eclipse.swt.events.PaintEvent; >-import org.eclipse.swt.events.PaintListener; >-import org.eclipse.swt.events.SelectionAdapter; >-import org.eclipse.swt.events.SelectionEvent; >-import org.eclipse.swt.events.SelectionListener; >-import org.eclipse.swt.graphics.Color; >-import org.eclipse.swt.widgets.Composite; >-import org.eclipse.swt.widgets.Control; >-import org.eclipse.swt.widgets.Display; >-import org.eclipse.swt.widgets.Event; >-import org.eclipse.swt.widgets.Item; >-import org.eclipse.zest.core.widgets.internal.ContainerFigure; >-import org.eclipse.zest.core.widgets.internal.RevealListener; >-import org.eclipse.zest.core.widgets.internal.ZestRootLayer; >-import org.eclipse.zest.layouts.InvalidLayoutConfiguration; >-import org.eclipse.zest.layouts.LayoutAlgorithm; >-import org.eclipse.zest.layouts.LayoutEntity; >-import org.eclipse.zest.layouts.LayoutRelationship; >-import org.eclipse.zest.layouts.LayoutStyles; >-import org.eclipse.zest.layouts.algorithms.TreeLayoutAlgorithm; >-import org.eclipse.zest.layouts.constraints.LayoutConstraint; >- >-/* >- * Holds the nodes and connections for the graph. >- * >- * @author Chris Callendar >- * >- * @author Ian Bull >- */ >-public class Graph extends FigureCanvas implements IContainer { >- >- // CLASS CONSTANTS >- public static final int ANIMATION_TIME = 500; >- public static final int FISHEYE_ANIMATION_TIME = 100; >- >- // @tag CGraph.Colors : These are the colour constants for the graph, they >- // are disposed on clean-up >- public Color LIGHT_BLUE = new Color(null, 216, 228, 248); >- public Color LIGHT_BLUE_CYAN = new Color(null, 213, 243, 255); >- public Color GREY_BLUE = new Color(null, 139, 150, 171); >- public Color DARK_BLUE = new Color(null, 1, 70, 122); >- public Color LIGHT_YELLOW = new Color(null, 255, 255, 206); >- >- public Color HIGHLIGHT_COLOR = ColorConstants.yellow; >- public Color HIGHLIGHT_ADJACENT_COLOR = ColorConstants.orange; >- public Color DEFAULT_NODE_COLOR = LIGHT_BLUE; >- >- /** >- * These are all the children of this graph. These lists contains all nodes >- * and connections that have added themselves to this graph. >- */ >- private final List nodes; >- protected List connections; >- private List selectedItems = null; >- IFigure fisheyedFigure = null; >- private List /* SelectionListener */selectionListeners = null; >- >- /** This maps all visible nodes to their model element. */ >- private HashMap figure2ItemMap = null; >- >- /** Maps user nodes to internal nodes */ >- private int connectionStyle; >- private int nodeStyle; >- private List constraintAdapters; >- private List revealListeners = null; >- >- private ScalableFreeformLayeredPane fishEyeLayer = null; >- LayoutAlgorithm layoutAlgorithm = null; >- private Dimension preferredSize = null; >- int style = 0; >- >- private ScalableFreeformLayeredPane rootlayer; >- private ZestRootLayer zestRootLayer; >- >- /** >- * Constructor for a Graph. This widget represents the root of the graph, >- * and can contain graph items such as graph nodes and graph connections. >- * >- * @param parent >- * @param style >- */ >- public Graph(Composite parent, int style) { >- super(parent, style | SWT.DOUBLE_BUFFERED); >- this.style = style; >- this.setBackground(ColorConstants.white); >- >- LIGHT_BLUE = new Color(Display.getDefault(), 216, 228, 248); >- LIGHT_BLUE_CYAN = new Color(Display.getDefault(), 213, 243, 255); >- GREY_BLUE = new Color(Display.getDefault(), 139, 150, 171); >- DARK_BLUE = new Color(Display.getDefault(), 1, 70, 122); >- LIGHT_YELLOW = new Color(Display.getDefault(), 255, 255, 206); >- >- this.setViewport(new FreeformViewport()); >- >- this.getVerticalBar().addSelectionListener(new SelectionAdapter() { >- public void widgetSelected(SelectionEvent e) { >- Graph.this.redraw(); >- } >- >- }); >- this.getHorizontalBar().addSelectionListener(new SelectionAdapter() { >- public void widgetSelected(SelectionEvent e) { >- Graph.this.redraw(); >- } >- }); >- >- // @tag CGraph.workaround : this allows me to handle mouse events >- // outside of the canvas >- this.getLightweightSystem().setEventDispatcher(new SWTEventDispatcher() { >- public void dispatchMouseMoved(org.eclipse.swt.events.MouseEvent me) { >- super.dispatchMouseMoved(me); >- >- // If the current event is null, return >- if (getCurrentEvent() == null) { >- return; >- } >- >- if (getMouseTarget() == null) { >- setMouseTarget(getRoot()); >- } >- if ((me.stateMask & SWT.BUTTON_MASK) != 0) { >- // Sometimes getCurrentEvent() returns null >- getMouseTarget().handleMouseDragged(getCurrentEvent()); >- } else { >- getMouseTarget().handleMouseMoved(getCurrentEvent()); >- } >- } >- }); >- >- this.setContents(createLayers()); >- DragSupport dragSupport = new DragSupport(this); >- this.getLightweightSystem().getRootFigure().addMouseListener(dragSupport); >- this.getLightweightSystem().getRootFigure().addMouseMotionListener(dragSupport); >- >- this.nodes = new ArrayList(); >- this.preferredSize = new Dimension(-1, -1); >- this.connectionStyle = ZestStyles.NONE; >- this.nodeStyle = ZestStyles.NONE; >- this.connections = new ArrayList(); >- this.constraintAdapters = new ArrayList(); >- this.selectedItems = new ArrayList(); >- this.selectionListeners = new ArrayList(); >- this.figure2ItemMap = new HashMap(); >- >- revealListeners = new ArrayList(1); >- this.addPaintListener(new PaintListener() { >- public void paintControl(PaintEvent e) { >- if (!revealListeners.isEmpty()) { >- // Go through the reveal list and let everyone know that the >- // view is now available. Remove the listeners so they are >- // only >- // called once! >- Iterator iterator = revealListeners.iterator(); >- while (iterator.hasNext()) { >- RevealListener reveallisetner = (RevealListener) iterator.next(); >- reveallisetner.revealed(Graph.this); >- iterator.remove(); >- } >- } >- /* >- Iterator iterator = getNodes().iterator(); >- while (iterator.hasNext()) { >- GraphNode node = (GraphNode) iterator.next(); >- node.paint(); >- } >- */ >- } >- }); >- >- } >- >- /** >- * This adds a listener to the set of listeners that will be called when a >- * selection event occurs. >- * >- * @param selectionListener >- */ >- public void addSelectionListener(SelectionListener selectionListener) { >- if (!selectionListeners.contains(selectionListener)) { >- selectionListeners.add(selectionListener); >- } >- } >- >- public void removeSelectionListener(SelectionListener selectionListener) { >- if (selectionListeners.contains(selectionListener)) { >- selectionListeners.remove(selectionListener); >- } >- } >- >- /** >- * Gets a list of the GraphModelNode children objects under the root node in >- * this diagram. If the root node is null then all the top level nodes are >- * returned. >- * >- * @return List of GraphModelNode objects >- */ >- public List getNodes() { >- return nodes; >- } >- >- /** >- * Adds a new constraint adapter to the list of constraint adapters >- * @param constraintAdapter >- */ >- public void addConstraintAdapter(ConstraintAdapter constraintAdapter) { >- this.constraintAdapters.add(constraintAdapter); >- } >- >- /** >- * Sets the constraint adapters on this model >- * >- * @param constraintAdapters >- */ >- public void setConstraintAdapters(List /* ConstraintAdapters */constraintAdapters) { >- this.constraintAdapters = constraintAdapters; >- } >- >- /** >- * Gets the root layer for this graph >- * >- * @return >- */ >- public ScalableFigure getRootLayer() { >- return rootlayer; >- } >- >- /** >- * Sets the default connection style. >- * >- * @param connection >- * style the connection style to set >- * @see org.eclipse.mylar.zest.core.widgets.ZestStyles >- */ >- public void setConnectionStyle(int connectionStyle) { >- this.connectionStyle = connectionStyle; >- } >- >- /** >- * Gets the default connection style. >- * >- * @return the connection style >- * @see org.eclipse.mylar.zest.core.widgets.ZestStyles >- */ >- public int getConnectionStyle() { >- return connectionStyle; >- } >- >- /** >- * Sets the default node style. >- * >- * @param nodeStyle >- * the node style to set >- * @see org.eclipse.mylar.zest.core.widgets.ZestStyles >- */ >- public void setNodeStyle(int nodeStyle) { >- this.nodeStyle = nodeStyle; >- } >- >- /** >- * Gets the default node style. >- * >- * @return the node style >- * @see org.eclipse.mylar.zest.core.widgets.ZestStyles >- */ >- public int getNodeStyle() { >- return nodeStyle; >- } >- >- /** >- * Gets the list of GraphModelConnection objects. >- * >- * @return list of GraphModelConnection objects >- */ >- public List getConnections() { >- return this.connections; >- } >- >- /** >- * Changes the selection to the list of items >- * >- * @param l >- */ >- public void setSelection(GraphItem[] nodes) { >- clearSelection(); >- if (nodes != null) { >- for (int i = 0; i < nodes.length; i++) { >- if (nodes[i] != null && nodes[i] instanceof GraphItem) { >- selectedItems.add(nodes[i]); >- (nodes[i]).highlight(); >- } >- } >- } >- } >- >- public void selectAll() { >- clearSelection(); >- for (int i = 0; i < nodes.size(); i++) { >- selectedItems.add(nodes.get(i)); >- ((GraphNode) nodes.get(i)).highlight(); >- } >- } >- >- /** >- * Gets the list of currently selected GraphNodes >- * >- * @return Currently selected graph node >- */ >- public List getSelection() { >- return selectedItems; >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.swt.widgets.Widget#toString() >- */ >- public String toString() { >- return "GraphModel {" + nodes.size() + " nodes, " + connections.size() + " connections}"; >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphItem#getGraphModel() >- */ >- public Graph getGraphModel() { >- return this; >- } >- >- /** >- * Dispose of the nodes and edges when the graph is disposed. >- */ >- public void dispose() { >- while (nodes.size() > 0) { >- GraphNode node = (GraphNode) nodes.get(0); >- if (node != null && !node.isDisposed()) { >- node.dispose(); >- } >- } >- while (connections.size() > 0) { >- GraphConnection connection = (GraphConnection) connections.get(0); >- if (connection != null && !connection.isDisposed()) { >- connection.dispose(); >- } >- } >- super.dispose(); >- >- LIGHT_BLUE.dispose(); >- LIGHT_BLUE_CYAN.dispose(); >- GREY_BLUE.dispose(); >- DARK_BLUE.dispose(); >- LIGHT_YELLOW.dispose(); >- } >- >- /** >- * Runs the layout on this graph. It uses the reveal listner to run the >- * layout only if the view is visible. Otherwise it will be deferred until >- * after the view is available. >- */ >- public void applyLayout() { >- this.addRevealListener(new RevealListener() { >- public void revealed(Control c) { >- Display.getDefault().asyncExec(new Runnable() { >- >- public void run() { >- applyLayoutInternal(); >- } >- }); >- } >- }); >- } >- >- /** >- * Sets the preferred size of the layout area. Size of ( -1, -1) uses the >- * current canvas size. >- * >- * @param width >- * @param height >- */ >- public void setPreferredSize(int width, int height) { >- this.preferredSize = new Dimension(width, height); >- } >- >- /** >- * @param algorithm >- */ >- public void setLayoutAlgorithm(LayoutAlgorithm algorithm, boolean applyLayout) { >- this.layoutAlgorithm = algorithm; >- if (applyLayout) { >- applyLayout(); >- } >- } >- >- public LayoutAlgorithm getLayoutAlgorithm() { >- return this.layoutAlgorithm; >- } >- >- /** >- * Finds a figure at the location X, Y in the graph >- * >- * This point should be translated to relative before calling findFigureAt >- */ >- public IFigure getFigureAt(int x, int y) { >- IFigure figureUnderMouse = this.getContents().findFigureAt(x, y, new TreeSearch() { >- >- public boolean accept(IFigure figure) { >- return true; >- } >- >- public boolean prune(IFigure figure) { >- IFigure parent = figure.getParent(); >- // @tag TODO Zest : change these to from getParent to their actual layer names >- >- if (parent == fishEyeLayer) { >- // If it node is on the fish eye layer, don't worry about it. >- return true; >- } >- if (parent instanceof ContainerFigure && figure instanceof PolylineConnection) { >- return false; >- } >- if (parent == zestRootLayer || parent == zestRootLayer.getParent() || parent == zestRootLayer.getParent().getParent()) { >- return false; >- } >- GraphItem item = (GraphItem) figure2ItemMap.get(figure); >- if (item != null && item.getItemType() == GraphItem.CONTAINER) { >- return false; >- } else if (figure instanceof FreeformLayer || parent instanceof FreeformLayer || figure instanceof ScrollPane || parent instanceof ScrollPane || parent instanceof ScalableFreeformLayeredPane || figure instanceof ScalableFreeformLayeredPane || figure instanceof FreeformViewport || >- parent instanceof FreeformViewport) { >- return false; >- } >- return true; >- } >- >- }); >- return figureUnderMouse; >- >- } >- >- // ///////////////////////////////////////////////////////////////////////////////// >- // PRIVATE METHODS. These are NON API >- // ///////////////////////////////////////////////////////////////////////////////// >- class DragSupport implements MouseMotionListener, org.eclipse.draw2d.MouseListener { >- /** >- * >- */ >- Graph graph = null; >- Point lastLocation = null; >- GraphItem fisheyedItem = null; >- boolean isDragging = false; >- >- DragSupport(Graph graph) { >- this.graph = graph; >- } >- >- public void mouseDragged(org.eclipse.draw2d.MouseEvent me) { >- if (!isDragging) { >- return; >- } >- Point mousePoint = new Point(me.x, me.y); >- Point tempPoint = mousePoint.getCopy(); >- if (selectedItems.size() > 0) { >- Iterator iterator = selectedItems.iterator(); >- while (iterator.hasNext()) { >- GraphItem item = (GraphItem) iterator.next(); >- if ((item.getItemType() == GraphItem.NODE) || (item.getItemType() == GraphItem.CONTAINER)) { >- // @tag Zest.selection Zest.move : This is where the node movement is tracked >- Point pointCopy = mousePoint.getCopy(); >- >- Point tempLastLocation = lastLocation.getCopy(); >- item.getFigure().getParent().translateToRelative(tempLastLocation); >- item.getFigure().getParent().translateFromParent(tempLastLocation); >- >- item.getFigure().getParent().translateToRelative(pointCopy); >- item.getFigure().getParent().translateFromParent(pointCopy); >- Point delta = new Point(pointCopy.x - tempLastLocation.x, pointCopy.y - tempLastLocation.y); >- if (item.getItemType() == GraphItem.NODE || item.getItemType() == GraphItem.CONTAINER) { >- GraphNode node = (GraphNode) item; >- node.setLocation(node.getLocation().x + delta.x, node.getLocation().y + delta.y); >- >- } >- /* >- else if (item.getItemType() == GraphItem.CONTAINER) { >- GraphContainer container = (GraphContainer) item; >- container.setLocation(container.getLocation().x + delta.x, container.getLocation().y + delta.y); >- } >- */ >- } else { >- // There is no movement for connection >- } >- } >- if (fisheyedFigure != null) { >- Point pointCopy = mousePoint.getCopy(); >- >- Point tempLastLocation = lastLocation.getCopy(); >- fisheyedFigure.translateToRelative(tempLastLocation); >- fisheyedFigure.translateFromParent(tempLastLocation); >- >- fisheyedFigure.translateToRelative(pointCopy); >- fisheyedFigure.translateFromParent(pointCopy); >- Point delta = new Point(pointCopy.x - tempLastLocation.x, pointCopy.y - tempLastLocation.y); >- Point point = new Point(fisheyedFigure.getBounds().x + delta.x, fisheyedFigure.getBounds().y + delta.y); >- fishEyeLayer.setConstraint(fisheyedFigure, new Rectangle(point, fisheyedFigure.getSize())); >- fishEyeLayer.getUpdateManager().performUpdate(); >- //fisheyedFigure.setBounds(new Rectangle(point2, fisheyedFigure.getSize())); >- //fisheyedFigure.setLocation(new Point(fisheyedFigure.getBounds().x + delta.x, fisheyedFigure.getBounds().y + delta.y)); >- } >- } >- lastLocation = tempPoint; >- //oldLocation = mousePoint; >- } >- >- public void mouseEntered(org.eclipse.draw2d.MouseEvent me) { >- >- } >- >- public void mouseExited(org.eclipse.draw2d.MouseEvent me) { >- >- } >- >- public void mouseHover(org.eclipse.draw2d.MouseEvent me) { >- >- } >- >- /** >- * This tracks whenever a mouse moves. The only thing we care about is >- * fisheye(ing) nodes. This means whenever the mouse moves we check if >- * we need to fisheye on a node or not. >- */ >- public void mouseMoved(org.eclipse.draw2d.MouseEvent me) { >- Point mousePoint = new Point(me.x, me.y); >- getRootLayer().translateToRelative(mousePoint); >- IFigure figureUnderMouse = getFigureAt(mousePoint.x, mousePoint.y); >- >- if (figureUnderMouse != null) { >- // There is a figure under this mouse >- GraphItem itemUnderMouse = (GraphItem) figure2ItemMap.get(figureUnderMouse); >- if (itemUnderMouse == fisheyedItem) { >- >- } else if (itemUnderMouse != null && itemUnderMouse.getItemType() == GraphItem.NODE) { >- fisheyedItem = itemUnderMouse; >- fisheyedFigure = ((GraphNode) itemUnderMouse).fishEye(true, true); >- if (fisheyedFigure == null) { >- // If there is no fisheye figure (this means that the node does not support a fish eye) >- // then remove the fisheyed item >- fisheyedItem = null; >- } >- } else if (fisheyedItem != null) { >- ((GraphNode) fisheyedItem).fishEye(false, true); >- fisheyedItem = null; >- fisheyedFigure = null; >- } >- } else { >- if (fisheyedItem != null) { >- ((GraphNode) fisheyedItem).fishEye(false, true); >- fisheyedItem = null; >- fisheyedFigure = null; >- } >- } >- } >- >- public void mouseDoubleClicked(org.eclipse.draw2d.MouseEvent me) { >- >- } >- >- public void mousePressed(org.eclipse.draw2d.MouseEvent me) { >- isDragging = true; >- Point mousePoint = new Point(me.x, me.y); >- lastLocation = mousePoint.getCopy(); >- >- getRootLayer().translateToRelative(mousePoint); >- >- if (me.getState() == org.eclipse.draw2d.MouseEvent.ALT) { >- double scale = getRootLayer().getScale(); >- scale *= 1.05; >- getRootLayer().setScale(scale); >- Point newMousePoint = mousePoint.getCopy().scale(1.05); >- Point delta = new Point(newMousePoint.x - mousePoint.x, newMousePoint.y - mousePoint.y); >- Point newViewLocation = getViewport().getViewLocation().getCopy().translate(delta); >- getViewport().setViewLocation(newViewLocation); >- lastLocation.scale(scale); >- >- clearSelection(); >- return; >- } else if (me.getState() == (org.eclipse.draw2d.MouseEvent.ALT | org.eclipse.draw2d.MouseEvent.SHIFT)) { >- double scale = getRootLayer().getScale(); >- scale /= 1.05; >- getRootLayer().setScale(scale); >- >- Point newMousePoint = mousePoint.getCopy().scale(1 / 1.05); >- Point delta = new Point(newMousePoint.x - mousePoint.x, newMousePoint.y - mousePoint.y); >- Point newViewLocation = getViewport().getViewLocation().getCopy().translate(delta); >- getViewport().setViewLocation(newViewLocation); >- clearSelection(); >- return; >- } else { >- boolean hasSelection = selectedItems.size() > 0; >- IFigure figureUnderMouse = getFigureAt(mousePoint.x, mousePoint.y); >- getRootLayer().translateFromParent(mousePoint); >- >- if (figureUnderMouse != null) { >- figureUnderMouse.getParent().translateFromParent(mousePoint); >- } >- // If the figure under the mouse is the canvas, and CTRL is not being held down, then select >- // nothing >- if (figureUnderMouse == null || figureUnderMouse == graph) { >- if (me.getState() != org.eclipse.draw2d.MouseEvent.CONTROL) { >- clearSelection(); >- if (hasSelection) { >- fireWidgetSelectedEvent(null); >- hasSelection = false; >- } >- } >- return; >- } >- >- GraphItem itemUnderMouse = (GraphItem) figure2ItemMap.get(figureUnderMouse); >- if (itemUnderMouse == null) { >- if (me.getState() != org.eclipse.draw2d.MouseEvent.CONTROL) { >- clearSelection(); >- if (hasSelection) { >- fireWidgetSelectedEvent(null); >- hasSelection = false; >- } >- } >- return; >- } >- if (selectedItems.contains(itemUnderMouse)) { >- // We have already selected this node, and CTRL is being held down, remove this selection >- // @tag Zest.selection : This deselects when you have CTRL pressed >- if (me.getState() == org.eclipse.draw2d.MouseEvent.CONTROL) { >- selectedItems.remove(itemUnderMouse); >- (itemUnderMouse).unhighlight(); >- fireWidgetSelectedEvent(itemUnderMouse); >- } >- return; >- } >- >- if (me.getState() != org.eclipse.draw2d.MouseEvent.CONTROL) { >- clearSelection(); >- } >- >- if (itemUnderMouse.getItemType() == GraphItem.NODE) { >- // @tag Zest.selection : This is where the nodes are selected >- selectedItems.add(itemUnderMouse); >- ((GraphNode) itemUnderMouse).highlight(); >- fireWidgetSelectedEvent(itemUnderMouse); >- } else if (itemUnderMouse.getItemType() == GraphItem.CONNECTION) { >- selectedItems.add(itemUnderMouse); >- ((GraphConnection) itemUnderMouse).highlight(); >- fireWidgetSelectedEvent(itemUnderMouse); >- >- } else if (itemUnderMouse.getItemType() == GraphItem.CONTAINER) { >- selectedItems.add(itemUnderMouse); >- ((GraphContainer) itemUnderMouse).highlight(); >- fireWidgetSelectedEvent(itemUnderMouse); >- } >- } >- >- } >- >- public void mouseReleased(org.eclipse.draw2d.MouseEvent me) { >- isDragging = false; >- >- } >- >- } >- >- private void clearSelection() { >- if (selectedItems.size() > 0) { >- Iterator iterator = selectedItems.iterator(); >- while (iterator.hasNext()) { >- GraphItem item = (GraphItem) iterator.next(); >- item.unhighlight(); >- iterator.remove(); >- } >- } >- } >- >- private void fireWidgetSelectedEvent(Item item) { >- Iterator iterator = selectionListeners.iterator(); >- while (iterator.hasNext()) { >- SelectionListener selectionListener = (SelectionListener) iterator.next(); >- Event swtEvent = new Event(); >- swtEvent.item = item; >- swtEvent.widget = this; >- SelectionEvent event = new SelectionEvent(swtEvent); >- selectionListener.widgetSelected(event); >- } >- >- } >- >- /** >- * Moves the edge to the highlight layer. This moves the edge above the >- * nodes >- * >- * @param connection >- */ >- void highlightEdge(GraphConnection connection) { >- IFigure figure = connection.getConnectionFigure(); >- if (figure != null && !connection.isHighlighted()) { >- zestRootLayer.highlightConnection(figure); >- } >- } >- >- /** >- * Moves the edge from the edge feedback layer back to the edge layer >- * >- * @param graphConnection >- */ >- void unhighlightEdge(GraphConnection connection) { >- IFigure figure = connection.getConnectionFigure(); >- if (figure != null && connection.isHighlighted()) { >- zestRootLayer.unHighlightConnection(figure); >- } >- } >- >- /** >- * Moves the node onto the node feedback layer >- * >- * @param node >- */ >- void highlightNode(GraphNode node) { >- IFigure figure = node.getNodeFigure(); >- if (figure != null && !node.isHighlighted()) { >- zestRootLayer.highlightNode(figure); >- } >- } >- >- /** >- * Moves the node onto the node feedback layer >- * >- * @param node >- */ >- void highlightNode(GraphContainer node) { >- IFigure figure = node.getNodeFigure(); >- if (figure != null && !node.isHighlighted()) { >- zestRootLayer.highlightNode(figure); >- } >- } >- >- /** >- * Moves the node off the node feedback layer >- * >- * @param node >- */ >- void unhighlightNode(GraphContainer node) { >- IFigure figure = node.getNodeFigure(); >- if (figure != null && node.isHighlighted()) { >- zestRootLayer.unHighlightNode(figure); >- } >- } >- >- /** >- * Moves the node off the node feedback layer >- * >- * @param node >- */ >- void unhighlightNode(GraphNode node) { >- IFigure figure = node.getNodeFigure(); >- if (figure != null && node.isHighlighted()) { >- zestRootLayer.unHighlightNode(figure); >- } >- } >- >- /** >- * Converts the list of GraphModelConnection objects into an array and >- * returns it. >- * >- * @return GraphModelConnection[] >- */ >- GraphConnection[] getConnectionsArray() { >- GraphConnection[] connsArray = new GraphConnection[connections.size()]; >- connsArray = (GraphConnection[]) connections.toArray(connsArray); >- return connsArray; >- } >- >- LayoutRelationship[] getConnectionsToLayout(List nodesToLayout) { >- // @tag zest.bug.156528-Filters.follows : make sure not to layout >- // filtered connections, if the style says so. >- LayoutRelationship[] entities; >- if (ZestStyles.checkStyle(style, ZestStyles.IGNORE_INVISIBLE_LAYOUT)) { >- LinkedList connectionList = new LinkedList(); >- for (Iterator i = this.getConnections().iterator(); i.hasNext();) { >- GraphConnection next = (GraphConnection) i.next(); >- if (next.isVisible() && nodesToLayout.contains(next.getSource()) && nodesToLayout.contains(next.getDestination())) { >- connectionList.add(next.getLayoutRelationship()); >- } >- } >- entities = (LayoutRelationship[]) connectionList.toArray(new LayoutRelationship[] {}); >- } else { >- LinkedList nodeList = new LinkedList(); >- for (Iterator i = this.getConnections().iterator(); i.hasNext();) { >- GraphConnection next = (GraphConnection) i.next(); >- if (nodesToLayout.contains(next.getSource()) && nodesToLayout.contains(next.getDestination())) { >- nodeList.add(next.getLayoutRelationship()); >- } >- } >- entities = (LayoutRelationship[]) nodeList.toArray(new LayoutRelationship[] {}); >- } >- return entities; >- } >- >- LayoutEntity[] getNodesToLayout(List nodes) { >- // @tag zest.bug.156528-Filters.follows : make sure not to layout >- // filtered nodes, if the style says so. >- LayoutEntity[] entities; >- if (ZestStyles.checkStyle(style, ZestStyles.IGNORE_INVISIBLE_LAYOUT)) { >- LinkedList nodeList = new LinkedList(); >- for (Iterator i = nodes.iterator(); i.hasNext();) { >- GraphNode next = (GraphNode) i.next(); >- if (next.isVisible()) { >- nodeList.add(next.getLayoutEntity()); >- } >- } >- entities = (LayoutEntity[]) nodeList.toArray(new LayoutEntity[] {}); >- } else { >- LinkedList nodeList = new LinkedList(); >- for (Iterator i = nodes.iterator(); i.hasNext();) { >- GraphNode next = (GraphNode) i.next(); >- nodeList.add(next.getLayoutEntity()); >- } >- entities = (LayoutEntity[]) nodeList.toArray(new LayoutEntity[] {}); >- } >- return entities; >- } >- >- void removeConnection(GraphConnection connection) { >- IFigure figure = connection.getConnectionFigure(); >- PolylineConnection sourceContainerConnectionFigure = connection.getSourceContainerConnectionFigure(); >- PolylineConnection targetContainerConnectionFigure = connection.getTargetContainerConnectionFigure(); >- connection.removeFigure(); >- this.getConnections().remove(connection); >- figure2ItemMap.remove(figure); >- if (sourceContainerConnectionFigure != null) { >- figure2ItemMap.remove(sourceContainerConnectionFigure); >- } >- if (targetContainerConnectionFigure != null) { >- figure2ItemMap.remove(targetContainerConnectionFigure); >- } >- } >- >- void removeNode(GraphNode node) { >- IFigure figure = node.getNodeFigure(); >- if (figure.getParent() != null) { >- if (figure.getParent() instanceof ZestRootLayer) { >- ((ZestRootLayer) figure.getParent()).removeNode(figure); >- } else { >- figure.getParent().remove(figure); >- } >- } >- this.getNodes().remove(node); >- figure2ItemMap.remove(figure); >- } >- >- void addConnection(GraphConnection connection, boolean addToEdgeLayer) { >- this.getConnections().add(connection); >- if (addToEdgeLayer) { >- zestRootLayer.addConnection(connection.getFigure()); >- } >- } >- >- /* >- public void redraw() { >- >- Iterator iterator = this.getConnections().iterator(); >- while (iterator.hasNext()) { >- GraphConnection connection = (GraphConnection) iterator.next(); >- IFigure figure = connection.getFigure(); >- if (!zestRootLayer.getChildren().contains(figure)) { >- if (true || false || false) { >- zestRootLayer.addConnection(connection.getFigure()); >- } >- } >- } >- iterator = this.getNodes().iterator(); >- while (iterator.hasNext()) { >- GraphNode graphNode = (GraphNode) iterator.next(); >- IFigure figure = graphNode.getFigure(); >- if (!zestRootLayer.getChildren().contains(figure)) { >- zestRootLayer.addNode(graphNode.getFigure()); >- } >- } >- >- super.redraw(); >- >- } >- */ >- >- void addNode(GraphNode node) { >- this.getNodes().add(node); >- zestRootLayer.addNode(node.getFigure()); >- } >- >- void addNode(GraphContainer graphContainer) { >- this.getNodes().add(graphContainer); >- zestRootLayer.addNode(graphContainer.getFigure()); >- >- } >- >- void registerItem(GraphItem item) { >- if (item.getItemType() == GraphItem.NODE) { >- IFigure figure = item.getFigure(); >- figure2ItemMap.put(figure, item); >- } else if (item.getItemType() == GraphItem.CONNECTION) { >- IFigure figure = item.getFigure(); >- figure2ItemMap.put(figure, item); >- if (((GraphConnection) item).getSourceContainerConnectionFigure() != null) { >- figure2ItemMap.put(((GraphConnection) item).getSourceContainerConnectionFigure(), item); >- } >- if (((GraphConnection) item).getTargetContainerConnectionFigure() != null) { >- figure2ItemMap.put(((GraphConnection) item).getTargetContainerConnectionFigure(), item); >- } >- } else if (item.getItemType() == GraphItem.CONTAINER) { >- IFigure figure = item.getFigure(); >- figure2ItemMap.put(figure, item); >- } else { >- throw new RuntimeException("Unknown item type: " + item.getItemType()); >- } >- } >- >- /* >- >- >- /** >- * Changes the figure for a particular node >- */ >- void changeNodeFigure(IFigure oldValue, IFigure newFigure, GraphNode graphItem) { >- if (zestRootLayer.getChildren().contains(oldValue)) { >- zestRootLayer.remove(oldValue); >- figure2ItemMap.remove(oldValue); >- } >- figure2ItemMap.put(newFigure, graphItem); >- zestRootLayer.add(newFigure); >- } >- >- /** >- * Invoke all the constraint adapaters for this constraints >- * >- * @param object >- * @param constraint >- */ >- void invokeConstraintAdapters(Object object, LayoutConstraint constraint) { >- if (constraintAdapters == null) { >- return; >- } >- Iterator iterator = this.constraintAdapters.iterator(); >- while (iterator.hasNext()) { >- ConstraintAdapter constraintAdapter = (ConstraintAdapter) iterator.next(); >- constraintAdapter.populateConstraint(object, constraint); >- } >- } >- >- private void applyLayoutInternal() { >- >- if ((this.getNodes().size() == 0)) { >- return; >- } >- >- int layoutStyle = 0; >- >- if ((nodeStyle & ZestStyles.NODES_NO_LAYOUT_RESIZE) > 0) { >- layoutStyle = LayoutStyles.NO_LAYOUT_NODE_RESIZING; >- } >- >- if (layoutAlgorithm == null) { >- layoutAlgorithm = new TreeLayoutAlgorithm(layoutStyle); >- } >- >- layoutAlgorithm.setStyle(layoutAlgorithm.getStyle() | layoutStyle); >- >- // calculate the size for the layout algorithm >- Dimension d = this.getViewport().getSize(); >- d.width = d.width - 10; >- d.height = d.height - 10; >- >- if (this.preferredSize.width >= 0) { >- d.width = preferredSize.width; >- } >- if (this.preferredSize.height >= 0) { >- d.height = preferredSize.height; >- } >- >- if (d.isEmpty()) { >- return; >- } >- LayoutRelationship[] connectionsToLayout = getConnectionsToLayout(nodes); >- LayoutEntity[] nodesToLayout = getNodesToLayout(getNodes()); >- >- try { >- Animation.markBegin(); >- layoutAlgorithm.applyLayout(nodesToLayout, connectionsToLayout, 0, 0, d.width, d.height, false, false); >- Animation.run(ANIMATION_TIME); >- getLightweightSystem().getUpdateManager().performUpdate(); >- >- } catch (InvalidLayoutConfiguration e) { >- e.printStackTrace(); >- } >- >- } >- >- interface MyRunnable extends Runnable { >- public boolean isVisible(); >- } >- >- /** >- * Adds a reveal listener to the view. Note: A reveal listener will only >- * every be called ONCE!!! even if a view comes and goes. There is no remove >- * reveal listener. This is used to defer some events until after the view >- * is revealed. >- * >- * @param revealListener >- */ >- private void addRevealListener(final RevealListener revealListener) { >- >- MyRunnable myRunnable = new MyRunnable() { >- boolean isVisible; >- >- public boolean isVisible() { >- return this.isVisible; >- } >- >- public void run() { >- isVisible = Graph.this.isVisible(); >- } >- >- }; >- Display.getDefault().syncExec(myRunnable); >- >- if (myRunnable.isVisible()) { >- revealListener.revealed(this); >- } else { >- revealListeners.add(revealListener); >- } >- } >- >- private ScalableFigure createLayers() { >- rootlayer = new ScalableFreeformLayeredPane(); >- rootlayer.setLayoutManager(new FreeformLayout()); >- zestRootLayer = new ZestRootLayer(); >- >- zestRootLayer.setLayoutManager(new FreeformLayout()); >- >- fishEyeLayer = new ScalableFreeformLayeredPane(); >- fishEyeLayer.setLayoutManager(new FreeformLayout()); >- >- rootlayer.add(zestRootLayer); >- rootlayer.add(fishEyeLayer); >- >- zestRootLayer.addLayoutListener(LayoutAnimator.getDefault()); >- fishEyeLayer.addLayoutListener(LayoutAnimator.getDefault()); >- return rootlayer; >- } >- >- /** >- * This removes the fisheye from the graph. It uses an animation to make the fisheye >- * shrink, and then it finally clears the fisheye layer. This assumes that there >- * is ever only 1 node on the fisheye layer at any time. >- * >- * @param fishEyeFigure The fisheye figure >- * @param regularFigure The regular figure (i.e. the non fisheye version) >- */ >- void removeFishEye(final IFigure fishEyeFigure, final IFigure regularFigure, boolean animate) { >- >- if (!fishEyeLayer.getChildren().contains(fishEyeFigure)) { >- return; >- } >- if (animate) { >- Animation.markBegin(); >- } >- >- Rectangle bounds = regularFigure.getBounds().getCopy(); >- regularFigure.translateToAbsolute(bounds); >- >- double scale = rootlayer.getScale(); >- fishEyeLayer.setScale(1 / scale); >- fishEyeLayer.translateToRelative(bounds); >- fishEyeLayer.translateFromParent(bounds); >- >- fishEyeLayer.setConstraint(fishEyeFigure, bounds); >- >- if (animate) { >- Animation.run(FISHEYE_ANIMATION_TIME * 2); >- } >- this.getRootLayer().getUpdateManager().performUpdate(); >- fishEyeLayer.removeAll(); >- this.fisheyedFigure = null; >- >- } >- >- /** >- * Replaces the old fisheye figure with a new one. >- * @param oldFigure >- * @param newFigure >- */ >- boolean replaceFishFigure(IFigure oldFigure, IFigure newFigure) { >- if (this.fishEyeLayer.getChildren().contains(oldFigure)) { >- Rectangle bounds = oldFigure.getBounds(); >- newFigure.setBounds(bounds); >- //this.fishEyeLayer.getChildren().remove(oldFigure); >- this.fishEyeLayer.remove(oldFigure); >- this.fishEyeLayer.add(newFigure); >- //this.fishEyeLayer.getChildren().add(newFigure); >- //this.fishEyeLayer.invalidate(); >- //this.fishEyeLayer.repaint(); >- this.fisheyedFigure = newFigure; >- return true; >- } >- return false; >- } >- >- /** >- * Add a fisheye version of the node. This works by animating the change from the original node >- * to the fisheyed one, and then placing the fisheye node on the fisheye layer. >- * @param startFigure The original node >- * @param endFigure The fisheye figure >- * @param newBounds The final size of the fisheyed figure >- */ >- void fishEye(IFigure startFigure, IFigure endFigure, Rectangle newBounds, boolean animate) { >- >- fishEyeLayer.removeAll(); >- fisheyedFigure = null; >- if (animate) { >- Animation.markBegin(); >- } >- >- double scale = rootlayer.getScale(); >- fishEyeLayer.setScale(1 / scale); >- >- fishEyeLayer.translateToRelative(newBounds); >- fishEyeLayer.translateFromParent(newBounds); >- >- Rectangle bounds = startFigure.getBounds().getCopy(); >- startFigure.translateToAbsolute(bounds); >- //startFigure.translateToRelative(bounds); >- fishEyeLayer.translateToRelative(bounds); >- fishEyeLayer.translateFromParent(bounds); >- >- endFigure.setLocation(bounds.getLocation()); >- endFigure.setSize(bounds.getSize()); >- fishEyeLayer.add(endFigure); >- fishEyeLayer.setConstraint(endFigure, newBounds); >- >- if (animate) { >- Animation.run(FISHEYE_ANIMATION_TIME); >- } >- this.getRootLayer().getUpdateManager().performUpdate(); >- } >- >- public Graph getGraph() { >- // @tag refactor : Is this method really needed >- return this.getGraphModel(); >- } >- >- public int getItemType() { >- return GraphItem.GRAPH; >- } >- >- GraphItem getGraphItem(IFigure figure) { >- return (GraphItem) figure2ItemMap.get(figure); >- } >- >-} >+/******************************************************************************* >+ * Copyright 2005-2010, 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 >+ * Mateusz Matela >+ ******************************************************************************/ >+package org.eclipse.zest.core.widgets; >+ >+import java.util.ArrayList; >+import java.util.HashMap; >+import java.util.HashSet; >+import java.util.Iterator; >+import java.util.List; >+ >+import org.eclipse.draw2d.Animation; >+import org.eclipse.draw2d.ColorConstants; >+import org.eclipse.draw2d.CoordinateListener; >+import org.eclipse.draw2d.FigureCanvas; >+import org.eclipse.draw2d.FreeformLayer; >+import org.eclipse.draw2d.FreeformLayout; >+import org.eclipse.draw2d.FreeformViewport; >+import org.eclipse.draw2d.IFigure; >+import org.eclipse.draw2d.LayoutAnimator; >+import org.eclipse.draw2d.MouseMotionListener; >+import org.eclipse.draw2d.PolylineConnection; >+import org.eclipse.draw2d.SWTEventDispatcher; >+import org.eclipse.draw2d.ScalableFigure; >+import org.eclipse.draw2d.ScalableFreeformLayeredPane; >+import org.eclipse.draw2d.ScrollPane; >+import org.eclipse.draw2d.TreeSearch; >+import org.eclipse.draw2d.geometry.Dimension; >+import org.eclipse.draw2d.geometry.Point; >+import org.eclipse.draw2d.geometry.Rectangle; >+import org.eclipse.swt.SWT; >+import org.eclipse.swt.events.ControlEvent; >+import org.eclipse.swt.events.ControlListener; >+import org.eclipse.swt.events.PaintEvent; >+import org.eclipse.swt.events.PaintListener; >+import org.eclipse.swt.events.SelectionAdapter; >+import org.eclipse.swt.events.SelectionEvent; >+import org.eclipse.swt.events.SelectionListener; >+import org.eclipse.swt.graphics.Color; >+import org.eclipse.swt.widgets.Composite; >+import org.eclipse.swt.widgets.Display; >+import org.eclipse.swt.widgets.Event; >+import org.eclipse.swt.widgets.Item; >+import org.eclipse.swt.widgets.Widget; >+import org.eclipse.zest.core.widgets.internal.ContainerFigure; >+import org.eclipse.zest.core.widgets.internal.ZestRootLayer; >+import org.eclipse.zest.layouts.LayoutAlgorithm; >+import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle; >+import org.eclipse.zest.layouts.interfaces.ExpandCollapseManager; >+ >+/** >+ * @since 1.0 >+ */ >+public class Graph extends FigureCanvas implements IContainer { >+ >+ // CLASS CONSTANTS >+ public static final int ANIMATION_TIME = 500; >+ public static final int FISHEYE_ANIMATION_TIME = 100; >+ >+ // @tag CGraph.Colors : These are the colour constants for the graph, they >+ // are disposed on clean-up >+ public Color LIGHT_BLUE = new Color(null, 216, 228, 248); >+ public Color LIGHT_BLUE_CYAN = new Color(null, 213, 243, 255); >+ public Color GREY_BLUE = new Color(null, 139, 150, 171); >+ public Color DARK_BLUE = new Color(null, 1, 70, 122); >+ public Color LIGHT_YELLOW = new Color(null, 255, 255, 206); >+ >+ public Color HIGHLIGHT_COLOR = ColorConstants.yellow; >+ public Color HIGHLIGHT_ADJACENT_COLOR = ColorConstants.orange; >+ public Color DEFAULT_NODE_COLOR = LIGHT_BLUE; >+ >+ /** >+ * These are all the children of this graph. These lists contains all nodes >+ * and connections that have added themselves to this graph. >+ */ >+ private List nodes; >+ protected List connections; >+ HashSet subgraphFigures; >+ private List selectedItems = null; >+ private ArrayList fisheyeListeners = new ArrayList(); >+ private List selectionListeners = null; >+ >+ /** This maps all visible nodes to their model element. */ >+ private HashMap figure2ItemMap = null; >+ >+ private int connectionStyle; >+ private int nodeStyle; >+ private ScalableFreeformLayeredPane fishEyeLayer = null; >+ private InternalLayoutContext layoutContext = null; >+ private volatile boolean shouldSheduleLayout; >+ private volatile Runnable scheduledLayoutRunnable = null; >+ private volatile boolean scheduledLayoutClean = false; >+ private Dimension preferredSize = null; >+ int style = 0; >+ >+ private ScalableFreeformLayeredPane rootlayer; >+ private ZestRootLayer zestRootLayer; >+ >+ /** >+ * Constructor for a Graph. This widget represents the root of the graph, >+ * and can contain graph items such as graph nodes and graph connections. >+ * >+ * @param parent >+ * @param style >+ */ >+ public Graph(Composite parent, int style) { >+ super(parent, style | SWT.DOUBLE_BUFFERED); >+ this.style = style; >+ this.setBackground(ColorConstants.white); >+ >+ this.setViewport(new FreeformViewport()); >+ >+ this.getVerticalBar().addSelectionListener(new SelectionAdapter() { >+ public void widgetSelected(SelectionEvent e) { >+ Graph.this.redraw(); >+ } >+ >+ }); >+ this.getHorizontalBar().addSelectionListener(new SelectionAdapter() { >+ public void widgetSelected(SelectionEvent e) { >+ Graph.this.redraw(); >+ } >+ }); >+ >+ // @tag CGraph.workaround : this allows me to handle mouse events >+ // outside of the canvas >+ this.getLightweightSystem().setEventDispatcher( >+ new SWTEventDispatcher() { >+ public void dispatchMouseMoved( >+ org.eclipse.swt.events.MouseEvent me) { >+ super.dispatchMouseMoved(me); >+ >+ // If the current event is null, return >+ if (getCurrentEvent() == null) { >+ return; >+ } >+ >+ if (getMouseTarget() == null) { >+ setMouseTarget(getRoot()); >+ } >+ if ((me.stateMask & SWT.BUTTON_MASK) != 0) { >+ // Sometimes getCurrentEvent() returns null >+ getMouseTarget().handleMouseDragged( >+ getCurrentEvent()); >+ } else { >+ getMouseTarget() >+ .handleMouseMoved(getCurrentEvent()); >+ } >+ } >+ }); >+ >+ this.setContents(createLayers()); >+ DragSupport dragSupport = new DragSupport(); >+ this.getLightweightSystem().getRootFigure().addMouseListener( >+ dragSupport); >+ this.getLightweightSystem().getRootFigure().addMouseMotionListener( >+ dragSupport); >+ >+ this.nodes = new ArrayList(); >+ this.preferredSize = new Dimension(-1, -1); >+ this.connectionStyle = ZestStyles.NONE; >+ this.nodeStyle = ZestStyles.NONE; >+ this.connections = new ArrayList(); >+ this.subgraphFigures = new HashSet(); >+ this.selectedItems = new ArrayList(); >+ this.selectionListeners = new ArrayList(); >+ this.figure2ItemMap = new HashMap(); >+ >+ this.addPaintListener(new PaintListener() { >+ public void paintControl(PaintEvent e) { >+ if (shouldSheduleLayout) { >+ applyLayoutInternal(true); >+ shouldSheduleLayout = false; >+ } >+ } >+ }); >+ >+ this.addControlListener(new ControlListener() { >+ >+ public void controlResized(ControlEvent e) { >+ if (preferredSize.width == -1 || preferredSize.height == -1) { >+ getLayoutContext().fireBoundsChangedEvent(); >+ } >+ } >+ >+ public void controlMoved(ControlEvent e) { >+ // do nothing >+ } >+ }); >+ } >+ >+ /** >+ * This adds a listener to the set of listeners that will be called when a >+ * selection event occurs. >+ * >+ * @param selectionListener >+ */ >+ public void addSelectionListener(SelectionListener selectionListener) { >+ if (!selectionListeners.contains(selectionListener)) { >+ selectionListeners.add(selectionListener); >+ } >+ } >+ >+ public void removeSelectionListener(SelectionListener selectionListener) { >+ if (selectionListeners.contains(selectionListener)) { >+ selectionListeners.remove(selectionListener); >+ } >+ } >+ >+ /** >+ * Gets a list of the GraphModelNode children objects under the root node in >+ * this diagram. If the root node is null then all the top level nodes are >+ * returned. >+ * >+ * @return List of GraphModelNode objects >+ */ >+ public List getNodes() { >+ return nodes; >+ } >+ >+ /** >+ * Gets the root layer for this graph >+ * >+ * @return >+ */ >+ public ScalableFigure getRootLayer() { >+ return rootlayer; >+ } >+ >+ /** >+ * Sets the default connection style. >+ * >+ * @param connection >+ * style the connection style to set >+ * @see org.eclipse.mylar.zest.core.widgets.ZestStyles >+ */ >+ public void setConnectionStyle(int connectionStyle) { >+ this.connectionStyle = connectionStyle; >+ } >+ >+ /** >+ * Gets the default connection style. >+ * >+ * @return the connection style >+ * @see org.eclipse.mylar.zest.core.widgets.ZestStyles >+ */ >+ public int getConnectionStyle() { >+ return connectionStyle; >+ } >+ >+ /** >+ * Sets the default node style. >+ * >+ * @param nodeStyle >+ * the node style to set >+ * @see org.eclipse.mylar.zest.core.widgets.ZestStyles >+ */ >+ public void setNodeStyle(int nodeStyle) { >+ this.nodeStyle = nodeStyle; >+ } >+ >+ /** >+ * Gets the default node style. >+ * >+ * @return the node style >+ * @see org.eclipse.mylar.zest.core.widgets.ZestStyles >+ */ >+ public int getNodeStyle() { >+ return nodeStyle; >+ } >+ >+ /** >+ * Gets the list of GraphModelConnection objects. >+ * >+ * @return list of GraphModelConnection objects >+ */ >+ public List getConnections() { >+ return this.connections; >+ } >+ >+ /** >+ * Changes the selection to the list of items >+ * >+ * @param l >+ */ >+ public void setSelection(GraphItem[] nodes) { >+ clearSelection(); >+ if (nodes != null) { >+ for (int i = 0; i < nodes.length; i++) { >+ if (nodes[i] != null && nodes[i] instanceof GraphItem) { >+ selectedItems.add(nodes[i]); >+ (nodes[i]).highlight(); >+ } >+ } >+ } >+ // TODO shouldn't this method fire a selection event? >+ } >+ >+ public void selectAll() { >+ clearSelection(); >+ for (int i = 0; i < nodes.size(); i++) { >+ selectedItems.add(nodes.get(i)); >+ ((GraphNode) nodes.get(i)).highlight(); >+ } >+ // TODO shouldn't this method fire a selection event? >+ } >+ >+ /** >+ * Gets the list of currently selected GraphNodes >+ * >+ * @return Currently selected graph node >+ */ >+ public List getSelection() { >+ return selectedItems; >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see org.eclipse.swt.widgets.Widget#toString() >+ */ >+ public String toString() { >+ return "GraphModel {" + nodes.size() + " nodes, " + connections.size() >+ + " connections}"; >+ } >+ >+ /** >+ * Dispose of the nodes and edges when the graph is disposed. >+ */ >+ public void dispose() { >+ while (nodes.size() > 0) { >+ GraphNode node = (GraphNode) nodes.get(0); >+ if (node != null && !node.isDisposed()) { >+ node.dispose(); >+ } >+ } >+ while (connections.size() > 0) { >+ GraphConnection connection = (GraphConnection) connections.get(0); >+ if (connection != null && !connection.isDisposed()) { >+ connection.dispose(); >+ } >+ } >+ super.dispose(); >+ >+ LIGHT_BLUE.dispose(); >+ LIGHT_BLUE_CYAN.dispose(); >+ GREY_BLUE.dispose(); >+ DARK_BLUE.dispose(); >+ LIGHT_YELLOW.dispose(); >+ } >+ >+ /** >+ * Runs the layout on this graph. If the view is not visible layout will be >+ * deferred until after the view is available. >+ */ >+ public void applyLayout() { >+ scheduleLayoutOnReveal(true); >+ } >+ >+ /** >+ * @since 2.0 >+ */ >+ public void applyLayoutNow() { >+ getLayoutContext().applyLayout(true); >+ layoutContext.flushChanges(false); >+ } >+ >+ /** >+ * Enables or disables dynamic layout (that is layout algorithm performing >+ * layout in background or when certain events occur). Dynamic layout should >+ * be disabled before doing a long series of changes in the graph to make >+ * sure that layout algorithm won't interfere with these changes. >+ * >+ * Enabling dynamic layout causes the layout algorithm to be applied even if >+ * it's not actually a dynamic algorithm. >+ * >+ * @param enabled >+ * >+ * @since 2.0 >+ */ >+ public void setDynamicLayout(boolean enabled) { >+ if (getLayoutContext().isBackgroundLayoutEnabled() != enabled) { >+ layoutContext.setBackgroundLayoutEnabled(enabled); >+ if (enabled) { >+ scheduleLayoutOnReveal(false); >+ } >+ } >+ } >+ >+ /** >+ * >+ * @return true if dynamic layout is enabled (see >+ * {@link #setDynamicLayout(boolean)}) >+ * @since 2.0 >+ */ >+ public boolean isDynamicLayoutEnabled() { >+ return getLayoutContext().isBackgroundLayoutEnabled(); >+ } >+ >+ private void applyLayoutInternal(boolean clean) { >+ if (getLayoutContext().getLayoutAlgorithm() == null) { >+ return; >+ } >+ scheduledLayoutClean = scheduledLayoutClean || clean; >+ synchronized (this) { >+ if (scheduledLayoutRunnable == null) { >+ Display.getDefault().asyncExec( >+ scheduledLayoutRunnable = new Runnable() { >+ public void run() { >+ Animation.markBegin(); >+ getLayoutContext().applyLayout( >+ scheduledLayoutClean); >+ layoutContext.flushChanges(false); >+ Animation.run(ANIMATION_TIME); >+ getLightweightSystem().getUpdateManager() >+ .performUpdate(); >+ synchronized (Graph.this) { >+ scheduledLayoutRunnable = null; >+ scheduledLayoutClean = false; >+ } >+ } >+ }); >+ } >+ } >+ } >+ >+ /** >+ * Sets the preferred size of the layout area. Size of ( -1, -1) uses the >+ * current canvas size. >+ * >+ * @param width >+ * @param height >+ */ >+ public void setPreferredSize(int width, int height) { >+ this.preferredSize = new Dimension(width, height); >+ getLayoutContext().fireBoundsChangedEvent(); >+ } >+ >+ /** >+ * @return the preferred size of the layout area. >+ * @since 2.0 >+ */ >+ public Dimension getPreferredSize() { >+ if (preferredSize.width < 0 || preferredSize.height < 0) { >+ org.eclipse.swt.graphics.Point size = getSize(); >+ double scale = getRootLayer().getScale(); >+ return new Dimension((int) (size.x / scale + 0.5), (int) (size.y >+ / scale + 0.5)); >+ } >+ return preferredSize; >+ } >+ >+ /** >+ * @noreference This method is not intended to be referenced by clients. >+ */ >+ public InternalLayoutContext getLayoutContext() { >+ if (layoutContext == null) { >+ layoutContext = new InternalLayoutContext(this); >+ } >+ return layoutContext; >+ } >+ >+ /** >+ * @param algorithm >+ * @since 2.0 >+ */ >+ public void setLayoutAlgorithm(LayoutAlgorithm algorithm, >+ boolean applyLayout) { >+ getLayoutContext().setLayoutAlgorithm(algorithm); >+ if (applyLayout) { >+ applyLayout(); >+ } >+ } >+ >+ /** >+ * @since 2.0 >+ */ >+ public LayoutAlgorithm getLayoutAlgorithm() { >+ return getLayoutContext().getLayoutAlgorithm(); >+ } >+ >+ /** >+ * @since 2.0 >+ */ >+ public void setSubgraphFactory(SubgraphFactory factory) { >+ getLayoutContext().setSubgraphFactory(factory); >+ } >+ >+ /** >+ * @since 2.0 >+ */ >+ public SubgraphFactory getSubgraphFactory() { >+ return getLayoutContext().getSubgraphFactory(); >+ } >+ >+ /** >+ * @since 2.0 >+ */ >+ public void setExpandCollapseManager( >+ ExpandCollapseManager expandCollapseManager) { >+ getLayoutContext().setExpandCollapseManager(expandCollapseManager); >+ } >+ >+ /** >+ * @since 2.0 >+ */ >+ public ExpandCollapseManager getExpandCollapseManager() { >+ return getLayoutContext().getExpandCollapseManager(); >+ } >+ >+ /** >+ * Adds a filter used for hiding elements from layout algorithm. >+ * >+ * NOTE: If a node or subgraph if filtered out, all connections adjacent to >+ * it should also be filtered out. Otherwise layout algorithm may behave in >+ * an unexpected way. >+ * >+ * @param filter >+ * filter to add >+ * @since 2.0 >+ */ >+ public void addLayoutFilter(LayoutFilter filter) { >+ getLayoutContext().addFilter(filter); >+ } >+ >+ /** >+ * Removes given layout filter. If it had not been added to this graph, this >+ * method does nothing. >+ * >+ * @param filter >+ * filter to remove >+ * @since 2.0 >+ */ >+ public void removeLayoutFilter(LayoutFilter filter) { >+ getLayoutContext().removeFilter(filter); >+ } >+ >+ /** >+ * Finds a figure at the location X, Y in the graph >+ * >+ * This point should be translated to relative before calling findFigureAt >+ */ >+ public IFigure getFigureAt(int x, int y) { >+ IFigure figureUnderMouse = this.getContents().findFigureAt(x, y, >+ new TreeSearch() { >+ >+ public boolean accept(IFigure figure) { >+ return true; >+ } >+ >+ public boolean prune(IFigure figure) { >+ IFigure parent = figure.getParent(); >+ // @tag TODO Zest : change these to from getParent to >+ // their >+ // actual layer names >+ >+ if (parent == fishEyeLayer) { >+ // If it node is on the fish eye layer, don't worry >+ // about >+ // it. >+ return true; >+ } >+ if (parent instanceof ContainerFigure >+ && figure instanceof PolylineConnection) { >+ return false; >+ } >+ if (parent == zestRootLayer >+ || parent == zestRootLayer.getParent() >+ || parent == zestRootLayer.getParent() >+ .getParent()) { >+ return false; >+ } >+ GraphItem item = (GraphItem) figure2ItemMap.get(figure); >+ if (item != null >+ && item.getItemType() == GraphItem.CONTAINER) { >+ return false; >+ } else if (figure instanceof FreeformLayer >+ || parent instanceof FreeformLayer >+ || figure instanceof ScrollPane >+ || parent instanceof ScrollPane >+ || parent instanceof ScalableFreeformLayeredPane >+ || figure instanceof ScalableFreeformLayeredPane >+ || figure instanceof FreeformViewport >+ || parent instanceof FreeformViewport) { >+ return false; >+ } >+ return true; >+ } >+ >+ }); >+ return figureUnderMouse; >+ >+ } >+ >+ private class DragSupport implements MouseMotionListener, >+ org.eclipse.draw2d.MouseListener { >+ >+ Point dragStartLocation = null; >+ IFigure draggedSubgraphFigure = null; >+ /** locations of dragged items relative to cursor position */ >+ ArrayList relativeLocations = new ArrayList(); >+ GraphItem fisheyedItem = null; >+ boolean isDragging = false; >+ >+ public void mouseDragged(org.eclipse.draw2d.MouseEvent me) { >+ if (!isDragging) { >+ return; >+ } >+ if (selectedItems.isEmpty()) { >+ IFigure figureUnderMouse = getFigureAt(dragStartLocation.x, >+ dragStartLocation.y); >+ if (subgraphFigures.contains(figureUnderMouse)) { >+ draggedSubgraphFigure = figureUnderMouse; >+ } >+ } >+ >+ Point mousePoint = new Point(me.x, me.y); >+ if (!selectedItems.isEmpty() || draggedSubgraphFigure != null) { >+ >+ if (relativeLocations.isEmpty()) { >+ for (Iterator iterator = selectedItems.iterator(); iterator >+ .hasNext();) { >+ GraphItem item = (GraphItem) iterator.next(); >+ if ((item.getItemType() == GraphItem.NODE) >+ || (item.getItemType() == GraphItem.CONTAINER)) { >+ relativeLocations.add(getRelativeLocation(item >+ .getFigure())); >+ } >+ } >+ if (draggedSubgraphFigure != null) { >+ relativeLocations >+ .add(getRelativeLocation(draggedSubgraphFigure)); >+ } >+ } >+ >+ Iterator locationsIterator = relativeLocations.iterator(); >+ for (Iterator selectionIterator = selectedItems.iterator(); selectionIterator >+ .hasNext();) { >+ GraphItem item = (GraphItem) selectionIterator.next(); >+ if ((item.getItemType() == GraphItem.NODE) >+ || (item.getItemType() == GraphItem.CONTAINER)) { >+ Point pointCopy = mousePoint.getCopy(); >+ Point relativeLocation = (Point) locationsIterator >+ .next(); >+ >+ item.getFigure().getParent().translateToRelative( >+ pointCopy); >+ item.getFigure().getParent().translateFromParent( >+ pointCopy); >+ >+ ((GraphNode) item) >+ .setLocation(relativeLocation.x + pointCopy.x, >+ relativeLocation.y + pointCopy.y); >+ } else { >+ // There is no movement for connection >+ } >+ } >+ if (draggedSubgraphFigure != null) { >+ Point pointCopy = mousePoint.getCopy(); >+ draggedSubgraphFigure.getParent().translateToRelative( >+ pointCopy); >+ draggedSubgraphFigure.getParent().translateFromParent( >+ pointCopy); >+ Point relativeLocation = (Point) locationsIterator.next(); >+ pointCopy.x += relativeLocation.x; >+ pointCopy.y += relativeLocation.y; >+ >+ draggedSubgraphFigure.setLocation(pointCopy); >+ } >+ } >+ } >+ >+ private Point getRelativeLocation(IFigure figure) { >+ Point location = figure.getBounds().getTopLeft(); >+ Point mousePointCopy = dragStartLocation.getCopy(); >+ figure.getParent().translateToRelative(mousePointCopy); >+ figure.getParent().translateFromParent(mousePointCopy); >+ location.x -= mousePointCopy.x; >+ location.y -= mousePointCopy.y; >+ return location; >+ } >+ >+ public void mouseEntered(org.eclipse.draw2d.MouseEvent me) { >+ >+ } >+ >+ public void mouseExited(org.eclipse.draw2d.MouseEvent me) { >+ >+ } >+ >+ public void mouseHover(org.eclipse.draw2d.MouseEvent me) { >+ >+ } >+ >+ /** >+ * This tracks whenever a mouse moves. The only thing we care about is >+ * fisheye(ing) nodes. This means whenever the mouse moves we check if >+ * we need to fisheye on a node or not. >+ */ >+ public void mouseMoved(org.eclipse.draw2d.MouseEvent me) { >+ Point mousePoint = new Point(me.x, me.y); >+ getRootLayer().translateToRelative(mousePoint); >+ IFigure figureUnderMouse = getFigureAt(mousePoint.x, mousePoint.y); >+ >+ if (figureUnderMouse != null) { >+ // There is a figure under this mouse >+ GraphItem itemUnderMouse = (GraphItem) figure2ItemMap >+ .get(figureUnderMouse); >+ if (itemUnderMouse == fisheyedItem) { >+ return; >+ } >+ if (fisheyedItem != null) { >+ ((GraphNode) fisheyedItem).fishEye(false, true); >+ fisheyedItem = null; >+ } >+ if (itemUnderMouse != null >+ && itemUnderMouse.getItemType() == GraphItem.NODE) { >+ fisheyedItem = itemUnderMouse; >+ IFigure fisheyedFigure = ((GraphNode) itemUnderMouse) >+ .fishEye(true, true); >+ if (fisheyedFigure == null) { >+ // If there is no fisheye figure (this means that the >+ // node does not support a fish eye) >+ // then remove the fisheyed item >+ fisheyedItem = null; >+ } >+ } >+ } else { >+ if (fisheyedItem != null) { >+ ((GraphNode) fisheyedItem).fishEye(false, true); >+ fisheyedItem = null; >+ } >+ } >+ } >+ >+ public void mouseDoubleClicked(org.eclipse.draw2d.MouseEvent me) { >+ >+ } >+ >+ public void mousePressed(org.eclipse.draw2d.MouseEvent me) { >+ isDragging = true; >+ Point mousePoint = new Point(me.x, me.y); >+ dragStartLocation = mousePoint.getCopy(); >+ >+ getRootLayer().translateToRelative(mousePoint); >+ >+ if (me.getState() == org.eclipse.draw2d.MouseEvent.ALT) { >+ double scale = getRootLayer().getScale(); >+ scale *= 1.05; >+ getRootLayer().setScale(scale); >+ Point newMousePoint = mousePoint.getCopy().scale(1.05); >+ Point delta = new Point(newMousePoint.x - mousePoint.x, >+ newMousePoint.y - mousePoint.y); >+ Point newViewLocation = getViewport().getViewLocation() >+ .getCopy().translate(delta); >+ getViewport().setViewLocation(newViewLocation); >+ >+ clearSelection(); >+ return; >+ } else if (me.getState() == (org.eclipse.draw2d.MouseEvent.ALT | org.eclipse.draw2d.MouseEvent.SHIFT)) { >+ double scale = getRootLayer().getScale(); >+ scale /= 1.05; >+ getRootLayer().setScale(scale); >+ >+ Point newMousePoint = mousePoint.getCopy().scale(1 / 1.05); >+ Point delta = new Point(newMousePoint.x - mousePoint.x, >+ newMousePoint.y - mousePoint.y); >+ Point newViewLocation = getViewport().getViewLocation() >+ .getCopy().translate(delta); >+ getViewport().setViewLocation(newViewLocation); >+ clearSelection(); >+ return; >+ } else { >+ boolean hasSelection = selectedItems.size() > 0; >+ IFigure figureUnderMouse = getFigureAt(mousePoint.x, >+ mousePoint.y); >+ getRootLayer().translateFromParent(mousePoint); >+ >+ if (figureUnderMouse != null) { >+ figureUnderMouse.getParent() >+ .translateFromParent(mousePoint); >+ } >+ // If the figure under the mouse is the canvas, and CTRL is not >+ // being held down, then select >+ // nothing >+ if (figureUnderMouse == null || figureUnderMouse == Graph.this) { >+ if (me.getState() != org.eclipse.draw2d.MouseEvent.CONTROL) { >+ clearSelection(); >+ if (hasSelection) { >+ fireWidgetSelectedEvent(null); >+ hasSelection = false; >+ } >+ } >+ return; >+ } >+ >+ GraphItem itemUnderMouse = (GraphItem) figure2ItemMap >+ .get(figureUnderMouse); >+ if (itemUnderMouse == null) { >+ if (me.getState() != org.eclipse.draw2d.MouseEvent.CONTROL) { >+ clearSelection(); >+ if (hasSelection) { >+ fireWidgetSelectedEvent(null); >+ hasSelection = false; >+ } >+ } >+ return; >+ } >+ if (selectedItems.contains(itemUnderMouse)) { >+ // We have already selected this node, and CTRL is being >+ // held down, remove this selection >+ // @tag Zest.selection : This deselects when you have CTRL >+ // pressed >+ if (me.getState() == org.eclipse.draw2d.MouseEvent.CONTROL) { >+ selectedItems.remove(itemUnderMouse); >+ (itemUnderMouse).unhighlight(); >+ fireWidgetSelectedEvent(itemUnderMouse); >+ } >+ return; >+ } >+ >+ if (me.getState() != org.eclipse.draw2d.MouseEvent.CONTROL) { >+ clearSelection(); >+ } >+ >+ if (itemUnderMouse.getItemType() == GraphItem.NODE) { >+ // @tag Zest.selection : This is where the nodes are >+ // selected >+ selectedItems.add(itemUnderMouse); >+ ((GraphNode) itemUnderMouse).highlight(); >+ fireWidgetSelectedEvent(itemUnderMouse); >+ } else if (itemUnderMouse.getItemType() == GraphItem.CONNECTION) { >+ selectedItems.add(itemUnderMouse); >+ ((GraphConnection) itemUnderMouse).highlight(); >+ fireWidgetSelectedEvent(itemUnderMouse); >+ >+ } else if (itemUnderMouse.getItemType() == GraphItem.CONTAINER) { >+ selectedItems.add(itemUnderMouse); >+ ((GraphContainer) itemUnderMouse).highlight(); >+ fireWidgetSelectedEvent(itemUnderMouse); >+ } >+ } >+ >+ } >+ >+ public void mouseReleased(org.eclipse.draw2d.MouseEvent me) { >+ isDragging = false; >+ relativeLocations.clear(); >+ draggedSubgraphFigure = null; >+ } >+ >+ } >+ >+ private void clearSelection() { >+ if (selectedItems.size() > 0) { >+ Iterator iterator = selectedItems.iterator(); >+ while (iterator.hasNext()) { >+ GraphItem item = (GraphItem) iterator.next(); >+ item.unhighlight(); >+ iterator.remove(); >+ } >+ } >+ } >+ >+ private void fireWidgetSelectedEvent(Item item) { >+ Iterator iterator = selectionListeners.iterator(); >+ while (iterator.hasNext()) { >+ SelectionListener selectionListener = (SelectionListener) iterator >+ .next(); >+ Event swtEvent = new Event(); >+ swtEvent.item = item; >+ swtEvent.widget = this; >+ SelectionEvent event = new SelectionEvent(swtEvent); >+ selectionListener.widgetSelected(event); >+ } >+ >+ } >+ >+ /** >+ * Converts the list of GraphModelConnection objects into an array and >+ * returns it. >+ * >+ * @return GraphModelConnection[] >+ */ >+ GraphConnection[] getConnectionsArray() { >+ GraphConnection[] connsArray = new GraphConnection[connections.size()]; >+ connsArray = (GraphConnection[]) connections.toArray(connsArray); >+ return connsArray; >+ } >+ >+ void removeConnection(GraphConnection connection) { >+ IFigure figure = connection.getConnectionFigure(); >+ PolylineConnection sourceContainerConnectionFigure = connection >+ .getSourceContainerConnectionFigure(); >+ PolylineConnection targetContainerConnectionFigure = connection >+ .getTargetContainerConnectionFigure(); >+ connection.removeFigure(); >+ this.getConnections().remove(connection); >+ this.selectedItems.remove(connection); >+ figure2ItemMap.remove(figure); >+ if (sourceContainerConnectionFigure != null) { >+ figure2ItemMap.remove(sourceContainerConnectionFigure); >+ } >+ if (targetContainerConnectionFigure != null) { >+ figure2ItemMap.remove(targetContainerConnectionFigure); >+ } >+ getLayoutContext().fireConnectionRemovedEvent(connection.getLayout()); >+ } >+ >+ void removeNode(GraphNode node) { >+ IFigure figure = node.getNodeFigure(); >+ if (figure.getParent() != null) { >+ figure.getParent().remove(figure); >+ } >+ this.getNodes().remove(node); >+ this.selectedItems.remove(node); >+ figure2ItemMap.remove(figure); >+ node.getLayout().dispose(); >+ } >+ >+ void addConnection(GraphConnection connection, boolean addToEdgeLayer) { >+ this.getConnections().add(connection); >+ if (addToEdgeLayer) { >+ zestRootLayer.addConnection(connection.getFigure()); >+ } >+ getLayoutContext().fireConnectionAddedEvent(connection.getLayout()); >+ } >+ >+ /** >+ * @noreference This method is not intended to be referenced by clients. >+ */ >+ public void addNode(GraphNode node) { >+ this.getNodes().add(node); >+ zestRootLayer.addNode(node.getFigure()); >+ getLayoutContext().fireNodeAddedEvent(node.getLayout()); >+ } >+ >+ /** >+ * @noreference This method is not intended to be referenced by clients. >+ */ >+ public void addSubgraphFigure(IFigure figure) { >+ zestRootLayer.addSubgraph(figure); >+ subgraphFigures.add(figure); >+ } >+ >+ void removeSubgraphFigure(IFigure figure) { >+ subgraphFigures.remove(figure); >+ figure.getParent().remove(figure); >+ } >+ >+ void registerItem(GraphItem item) { >+ if (item.getItemType() == GraphItem.NODE) { >+ IFigure figure = item.getFigure(); >+ figure2ItemMap.put(figure, item); >+ } else if (item.getItemType() == GraphItem.CONNECTION) { >+ IFigure figure = item.getFigure(); >+ figure2ItemMap.put(figure, item); >+ if (((GraphConnection) item).getSourceContainerConnectionFigure() != null) { >+ figure2ItemMap.put(((GraphConnection) item) >+ .getSourceContainerConnectionFigure(), item); >+ } >+ if (((GraphConnection) item).getTargetContainerConnectionFigure() != null) { >+ figure2ItemMap.put(((GraphConnection) item) >+ .getTargetContainerConnectionFigure(), item); >+ } >+ } else if (item.getItemType() == GraphItem.CONTAINER) { >+ IFigure figure = item.getFigure(); >+ figure2ItemMap.put(figure, item); >+ } else { >+ throw new RuntimeException("Unknown item type: " >+ + item.getItemType()); >+ } >+ } >+ >+ /** >+ * Schedules a layout to be performed after the view is revealed (or >+ * immediately, if the view is already revealed). >+ * >+ * @param clean >+ */ >+ private void scheduleLayoutOnReveal(final boolean clean) { >+ >+ final boolean[] isVisibleSync = new boolean[1]; >+ Display.getDefault().syncExec(new Runnable() { >+ public void run() { >+ isVisibleSync[0] = isVisible(); >+ } >+ }); >+ >+ if (isVisibleSync[0]) { >+ applyLayoutInternal(clean); >+ } else { >+ shouldSheduleLayout = true; >+ } >+ } >+ >+ private ScalableFigure createLayers() { >+ rootlayer = new ScalableFreeformLayeredPane(); >+ rootlayer.setLayoutManager(new FreeformLayout()); >+ zestRootLayer = new ZestRootLayer(); >+ >+ zestRootLayer.setLayoutManager(new FreeformLayout()); >+ >+ fishEyeLayer = new ScalableFreeformLayeredPane(); >+ fishEyeLayer.setLayoutManager(new FreeformLayout()); >+ >+ rootlayer.add(zestRootLayer); >+ rootlayer.add(fishEyeLayer); >+ >+ zestRootLayer.addLayoutListener(LayoutAnimator.getDefault()); >+ fishEyeLayer.addLayoutListener(LayoutAnimator.getDefault()); >+ >+ rootlayer.addCoordinateListener(new CoordinateListener() { >+ public void coordinateSystemChanged(IFigure source) { >+ if (preferredSize.width == -1 && preferredSize.height == -1) { >+ getLayoutContext().fireBoundsChangedEvent(); >+ } >+ } >+ }); >+ >+ return rootlayer; >+ } >+ >+ /** >+ * This removes the fisheye from the graph. It uses an animation to make the >+ * fisheye shrink, and then it finally clears the fisheye layer. This >+ * assumes that there is ever only 1 node on the fisheye layer at any time. >+ * >+ * @param fishEyeFigure >+ * The fisheye figure >+ * @param regularFigure >+ * The regular figure (i.e. the non fisheye version) >+ */ >+ void removeFishEye(final IFigure fishEyeFigure, >+ final IFigure regularFigure, boolean animate) { >+ >+ if (!fishEyeLayer.getChildren().contains(fishEyeFigure)) { >+ return; >+ } >+ if (animate) { >+ Animation.markBegin(); >+ } >+ >+ Rectangle bounds = regularFigure.getBounds().getCopy(); >+ regularFigure.translateToAbsolute(bounds); >+ >+ double scale = rootlayer.getScale(); >+ fishEyeLayer.setScale(1 / scale); >+ fishEyeLayer.translateToRelative(bounds); >+ fishEyeLayer.translateFromParent(bounds); >+ >+ fishEyeLayer.setConstraint(fishEyeFigure, bounds); >+ >+ for (Iterator iterator = fisheyeListeners.iterator(); iterator >+ .hasNext();) { >+ FisheyeListener listener = (FisheyeListener) iterator.next(); >+ listener.fisheyeRemoved(this, regularFigure, fishEyeFigure); >+ } >+ >+ if (animate) { >+ Animation.run(FISHEYE_ANIMATION_TIME * 2); >+ } >+ this.getRootLayer().getUpdateManager().performUpdate(); >+ fishEyeLayer.removeAll(); >+ >+ } >+ >+ /** >+ * Replaces the old fisheye figure with a new one. >+ * >+ * @param oldFigure >+ * @param newFigure >+ */ >+ boolean replaceFishFigure(IFigure oldFigure, IFigure newFigure) { >+ if (this.fishEyeLayer.getChildren().contains(oldFigure)) { >+ Rectangle bounds = oldFigure.getBounds(); >+ newFigure.setBounds(bounds); >+ this.fishEyeLayer.remove(oldFigure); >+ this.fishEyeLayer.add(newFigure); >+ >+ for (Iterator iterator = fisheyeListeners.iterator(); iterator >+ .hasNext();) { >+ FisheyeListener listener = (FisheyeListener) iterator.next(); >+ listener.fisheyeReplaced(this, oldFigure, newFigure); >+ } >+ >+ return true; >+ } >+ return false; >+ } >+ >+ /** >+ * Add a fisheye version of the node. This works by animating the change >+ * from the original node to the fisheyed one, and then placing the fisheye >+ * node on the fisheye layer. >+ * >+ * @param startFigure >+ * The original node >+ * @param endFigure >+ * The fisheye figure >+ * @param newBounds >+ * The final size of the fisheyed figure >+ */ >+ void fishEye(IFigure startFigure, IFigure endFigure, Rectangle newBounds, >+ boolean animate) { >+ >+ fishEyeLayer.removeAll(); >+ >+ if (animate) { >+ Animation.markBegin(); >+ } >+ >+ double scale = rootlayer.getScale(); >+ fishEyeLayer.setScale(1 / scale); >+ >+ fishEyeLayer.translateToRelative(newBounds); >+ fishEyeLayer.translateFromParent(newBounds); >+ >+ Rectangle bounds = startFigure.getBounds().getCopy(); >+ startFigure.translateToAbsolute(bounds); >+ // startFigure.translateToRelative(bounds); >+ fishEyeLayer.translateToRelative(bounds); >+ fishEyeLayer.translateFromParent(bounds); >+ >+ endFigure.setLocation(bounds.getLocation()); >+ endFigure.setSize(bounds.getSize()); >+ fishEyeLayer.add(endFigure); >+ fishEyeLayer.setConstraint(endFigure, newBounds); >+ >+ for (Iterator iterator = fisheyeListeners.iterator(); iterator >+ .hasNext();) { >+ FisheyeListener listener = (FisheyeListener) iterator.next(); >+ listener.fisheyeAdded(this, startFigure, endFigure); >+ } >+ >+ if (animate) { >+ Animation.run(FISHEYE_ANIMATION_TIME); >+ } >+ this.getRootLayer().getUpdateManager().performUpdate(); >+ } >+ >+ /** >+ * Adds a listener that will be notified when fisheyed figures change in >+ * this graph. >+ * >+ * @param listener >+ * listener to add >+ * @since 2.0 >+ */ >+ public void addFisheyeListener(FisheyeListener listener) { >+ fisheyeListeners.add(listener); >+ } >+ >+ /** >+ * @since 2.0 >+ */ >+ public void removeFisheyeListener(FisheyeListener listener) { >+ fisheyeListeners.remove(listener); >+ } >+ >+ public int getItemType() { >+ return GraphItem.GRAPH; >+ } >+ >+ GraphItem getGraphItem(IFigure figure) { >+ return (GraphItem) figure2ItemMap.get(figure); >+ } >+ >+ /** >+ * @since 2.0 >+ */ >+ public void setExpanded(GraphNode node, boolean expanded) { >+ layoutContext.setExpanded(node.getLayout(), expanded); >+ rootlayer.invalidate(); >+ } >+ >+ /** >+ * @since 2.0 >+ */ >+ public boolean canExpand(GraphNode node) { >+ return layoutContext.canExpand(node.getLayout()); >+ } >+ >+ /** >+ * @since 2.0 >+ */ >+ public boolean canCollapse(GraphNode node) { >+ return layoutContext.canCollapse(node.getLayout()); >+ } >+ >+ public Graph getGraph() { >+ return this; >+ } >+ >+ /** >+ * @since 2.0 >+ */ >+ public Widget getItem() { >+ return this; >+ } >+ >+ /** >+ * @since 2.0 >+ */ >+ public DisplayIndependentRectangle getLayoutBounds() { >+ Dimension preferredSize = this.getPreferredSize(); >+ return new DisplayIndependentRectangle(0, 0, preferredSize.width, >+ preferredSize.height); >+ } >+} >Index: src/org/eclipse/zest/core/widgets/GraphConnection.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/widgets/GraphConnection.java,v >retrieving revision 1.23 >diff -u -r1.23 GraphConnection.java >--- src/org/eclipse/zest/core/widgets/GraphConnection.java 2 Apr 2009 22:49:10 -0000 1.23 >+++ src/org/eclipse/zest/core/widgets/GraphConnection.java 15 Mar 2010 03:48:58 -0000 >@@ -1,718 +1,739 @@ >-/******************************************************************************* >- * Copyright 2005, 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.widgets; >- >-import org.eclipse.draw2d.ChopboxAnchor; >-import org.eclipse.draw2d.ColorConstants; >-import org.eclipse.draw2d.Connection; >-import org.eclipse.draw2d.Graphics; >-import org.eclipse.draw2d.IFigure; >-import org.eclipse.draw2d.Label; >-import org.eclipse.draw2d.Locator; >-import org.eclipse.draw2d.MidpointLocator; >-import org.eclipse.draw2d.PolygonDecoration; >-import org.eclipse.draw2d.PolylineConnection; >-import org.eclipse.draw2d.Shape; >-import org.eclipse.draw2d.geometry.Point; >-import org.eclipse.swt.graphics.Color; >-import org.eclipse.swt.graphics.Font; >-import org.eclipse.swt.widgets.Display; >-import org.eclipse.zest.core.widgets.internal.LoopAnchor; >-import org.eclipse.zest.core.widgets.internal.PolylineArcConnection; >-import org.eclipse.zest.core.widgets.internal.RoundedChopboxAnchor; >-import org.eclipse.zest.core.widgets.internal.ZestRootLayer; >-import org.eclipse.zest.layouts.LayoutBendPoint; >-import org.eclipse.zest.layouts.LayoutEntity; >-import org.eclipse.zest.layouts.LayoutRelationship; >-import org.eclipse.zest.layouts.constraints.LayoutConstraint; >- >-/* >- * This is the graph connection model which stores the source and destination >- * nodes and the properties of this connection (color, line width etc). >- * >- * @author Chris Callendar >- * >- * @author Ian Bull >- */ >-public class GraphConnection extends GraphItem { >- >- private Font font; >- private GraphNode sourceNode; >- private GraphNode destinationNode; >- >- private double weight; >- private Color color; >- private Color highlightColor; >- private Color foreground; >- private int lineWidth; >- private int lineStyle; >- private final Graph graphModel; >- >- private int connectionStyle; >- private int curveDepth; >- private boolean isDisposed = false; >- >- private Label connectionLabel = null; >- private Connection connectionFigure = null; >- private Connection sourceContainerConnectionFigure = null; >- private Connection targetContainerConnectionFigure = null; >- >- /** >- * The state of visibility set by the user. >- */ >- private boolean visible; >- >- private IFigure tooltip; >- >- private boolean highlighted; >- private GraphLayoutConnection layoutConnection = null; >- private boolean hasCustomTooltip; >- >- public GraphConnection(Graph graphModel, int style, GraphNode source, GraphNode destination) { >- super(graphModel, style); >- >- this.connectionStyle |= graphModel.getConnectionStyle(); >- this.connectionStyle |= style; >- this.sourceNode = source; >- this.destinationNode = destination; >- this.visible = true; >- this.color = ColorConstants.lightGray; >- this.foreground = ColorConstants.lightGray; >- this.highlightColor = graphModel.DARK_BLUE; >- this.lineWidth = 1; >- this.lineStyle = Graphics.LINE_SOLID; >- setWeight(weight); >- this.graphModel = graphModel; >- this.curveDepth = 0; >- this.layoutConnection = new GraphLayoutConnection(); >- this.font = Display.getDefault().getSystemFont(); >- registerConnection(source, destination); >- } >- >- private void registerConnection(GraphNode source, GraphNode destination) { >- if (source.getSourceConnections().contains(this)) { >- source.removeSourceConnection(this); >- } >- if (destination.getTargetConnections().contains(this)) { >- destination.removeTargetConnection(this); >- } >- (source).addSourceConnection(this); >- (destination).addTargetConnection(this); >- >- if (source.getParent().getItemType() == GraphItem.CONTAINER && destination.getParent().getItemType() == GraphItem.CONTAINER && (source.getParent() == destination.getParent())) { >- // 196189: Edges should not draw on the edge layer if both the src and dest are in the same container >- // https://bugs.eclipse.org/bugs/show_bug.cgi?id=196189 >- graphModel.addConnection(this, ZestRootLayer.EDGES_ON_TOP); >- } else { >- graphModel.addConnection(this, true); >- } >- >- if ((source.getParent()).getItemType() == GraphItem.CONTAINER) { >- // If the container of the source is a container, we need to draw another >- // arc on that arc layer >- sourceContainerConnectionFigure = doCreateFigure(); >- ((GraphContainer) source.getParent()).addConnectionFigure((PolylineConnection) sourceContainerConnectionFigure); >- this.setVisible(false); >- } >- >- if ((destination.getParent()).getItemType() == GraphItem.CONTAINER) { //&& src_destSameContainer == false) { >- // If the container of the source is a container, we need to draw another >- // arc on that arc layer >- targetContainerConnectionFigure = doCreateFigure(); >- ((GraphContainer) destination.getParent()).addConnectionFigure((PolylineConnection) targetContainerConnectionFigure); >- this.setVisible(false); >- } >- graphModel.getGraph().registerItem(this); >- } >- >- void removeFigure() { >- if (connectionFigure.getParent() != null) { >- if (connectionFigure.getParent() instanceof ZestRootLayer) { >- ((ZestRootLayer) connectionFigure.getParent()).removeConnection(connectionFigure); >- } else { >- connectionFigure.getParent().remove(connectionFigure); >- } >- } >- connectionFigure = null; >- if (sourceContainerConnectionFigure != null) { >- sourceContainerConnectionFigure.getParent().remove(sourceContainerConnectionFigure); >- sourceContainerConnectionFigure = null; >- } >- if (targetContainerConnectionFigure != null) { >- targetContainerConnectionFigure.getParent().remove(targetContainerConnectionFigure); >- targetContainerConnectionFigure = null; >- } >- >- } >- >- public void dispose() { >- super.dispose(); >- this.isDisposed = true; >- (getSource()).removeSourceConnection(this); >- (getDestination()).removeTargetConnection(this); >- graphModel.removeConnection(this); >- if (sourceContainerConnectionFigure != null) { >- sourceContainerConnectionFigure.getParent().remove(sourceContainerConnectionFigure); >- } >- if (targetContainerConnectionFigure != null) { >- targetContainerConnectionFigure.getParent().remove(targetContainerConnectionFigure); >- } >- } >- >- public boolean isDisposed() { >- return isDisposed; >- } >- >- public Connection getConnectionFigure() { >- if (connectionFigure == null) { >- connectionFigure = createFigure(); >- } >- return connectionFigure; >- } >- >- /** >- * Gets a proxy to this connection that can be used with the Zest layout >- * engine >- * >- * @return >- */ >- public LayoutRelationship getLayoutRelationship() { >- return this.layoutConnection; >- } >- >- /** >- * Gets the external connection object. >- * >- * @return Object >- */ >- public Object getExternalConnection() { >- return this.getData(); >- } >- >- /** >- * Returns a string like 'source -> destination' >- * >- * @return String >- */ >- public String toString() { >- String arrow = (isBidirectionalInLayout() ? " <--> " : " --> "); >- String src = (sourceNode != null ? sourceNode.getText() : "null"); >- String dest = (destinationNode != null ? destinationNode.getText() : "null"); >- String weight = " (weight=" + getWeightInLayout() + ")"; >- return ("GraphModelConnection: " + src + arrow + dest + weight); >- } >- >- /** >- * Returns the style of this connection. Valid styles are those that begin >- * with CONNECTION in ZestStyles. >- * >- * @return the style of this connection. >- * @see #ZestStyles >- */ >- public int getConnectionStyle() { >- return connectionStyle; >- } >- >- /** >- * Returns the style of this connection. Valid styles are those that begin >- * with CONNECTION in ZestStyles. >- * >- * @return the style of this connection. >- * @see #ZestStyles >- */ >- public void setConnectionStyle(int style) { >- this.connectionStyle = style; >- updateFigure(this.connectionFigure); >- } >- >- /** >- * Gets the weight of this connection. The weight must be in {-1, [0-1]}. A >- * weight of -1 means that there is no force/tension between the nodes. A >- * weight of 0 results in the maximum spring length being used (farthest >- * apart). A weight of 1 results in the minimum spring length being used >- * (closest together). >- * >- * @see org.eclipse.mylar.zest.layouts.LayoutRelationship#getWeightInLayout() >- * @return the weight: {-1, [0 - 1]}. >- */ >- public double getWeightInLayout() { >- return weight; >- } >- >- /** >- * Gets the font for the label on this connection >- * >- * @return >- */ >- public Font getFont() { >- return this.font; >- } >- >- /** >- * Sets the font for the label on this connection. >- * >- */ >- public void setFont(Font f) { >- this.font = f; >- } >- >- /** >- * Sets the weight for this connection. The weight must be in {-1, [0-1]}. A >- * weight of -1 means that there is no force/tension between the nodes. A >- * weight of 0 results in the maximum spring length being used (farthest >- * apart). A weight of 1 results in the minimum spring length being used >- * (closest together). >- * >- */ >- public void setWeight(double weight) { >- if (weight < 0) { >- this.weight = -1; >- } else if (weight > 1) { >- this.weight = 1; >- } else { >- this.weight = weight; >- } >- } >- >- /** >- * Returns the color of this connection. >- * >- * @return Color >- */ >- public Color getLineColor() { >- return color; >- } >- >- /** >- * Sets the highlight color. >- * >- * @param color >- * the color to use for highlighting. >- */ >- public void setHighlightColor(Color color) { >- this.highlightColor = color; >- } >- >- /** >- * @return the highlight color >- */ >- public Color getHighlightColor() { >- return highlightColor; >- } >- >- /** >- * Perminently sets the color of this line to the given color. This will >- * become the color of the line when it is not highlighted. If you would >- * like to temporarily change the color of the line, use changeLineColor. >- * >- * @param color >- * the color to be set. >- * @see changeLineColor(Color color) >- */ >- public void setLineColor(Color color) { >- this.foreground = color; >- changeLineColor(foreground); >- } >- >- /** >- * Sets the connection color. >- * >- * @param color >- */ >- public void changeLineColor(Color color) { >- this.color = color; >- updateFigure(connectionFigure); >- } >- >- /** >- * Sets the tooltip on this node. This tooltip will display if the mouse >- * hovers over the node. Setting the tooltip has no effect if a custom >- * figure has been set. >- */ >- public void setTooltip(IFigure tooltip) { >- hasCustomTooltip = true; >- this.tooltip = tooltip; >- updateFigure(connectionFigure); >- } >- >- /** >- * Gets the current tooltip for this node. The tooltip returned is >- * meaningless if a custom figure has been set. >- */ >- public IFigure getTooltip() { >- return this.tooltip; >- } >- >- /** >- * Returns the connection line width. >- * >- * @return int >- */ >- public int getLineWidth() { >- return lineWidth; >- } >- >- /** >- * Sets the connection line width. >- * >- * @param lineWidth >- */ >- public void setLineWidth(int lineWidth) { >- this.lineWidth = lineWidth; >- updateFigure(connectionFigure); >- } >- >- /** >- * Returns the connection line style. >- * >- * @return int >- */ >- public int getLineStyle() { >- return lineStyle; >- } >- >- /** >- * Sets the connection line style. >- * >- * @param lineStyle >- */ >- public void setLineStyle(int lineStyle) { >- this.lineStyle = lineStyle; >- updateFigure(connectionFigure); >- } >- >- /** >- * Gets the source node for this relationship >- * >- * @return GraphModelNode >- */ >- public GraphNode getSource() { >- return this.sourceNode; >- } >- >- /** >- * Gets the target node for this relationship >- * >- * @return GraphModelNode >- */ >- public GraphNode getDestination() { >- return this.destinationNode; >- } >- >- /** >- * Highlights this node. Uses the default highlight color. >- */ >- public void highlight() { >- if (highlighted) { >- return; >- } >- highlighted = true; >- updateFigure(connectionFigure); >- graphModel.highlightEdge(this); >- } >- >- /** >- * Unhighlights this node. Uses the default color. >- */ >- public void unhighlight() { >- if (!highlighted) { >- return; >- } >- highlighted = false; >- updateFigure(connectionFigure); >- graphModel.unhighlightEdge(this); >- } >- >- /** >- * Returns true if this connection is highlighted, false otherwise >- * >- * @return >- */ >- public boolean isHighlighted() { >- return highlighted; >- } >- >- /** >- * Gets the graph model that this connection is in >- * >- * @return The graph model that this connection is contained in >- */ >- public Graph getGraphModel() { >- return this.graphModel; >- } >- >- /** >- * Sets the curve depth of the arc. The curve depth is defined as >- * the maximum distance from any point on the chord (i.e. a vector >- * normal to the chord with magnitude d). >- * >- * If 0 is set, a Polyline Connection will be used, otherwise a >- * PolylineArcConnectoin will be used. Negative depths are also supported. >- * @param depth The depth of the curve >- */ >- public void setCurveDepth(int depth) { >- if (this.curveDepth == 0 && depth != 0 || this.curveDepth != 0 && depth == 0) { >- // There is currently no curve, so we have to create >- // a curved connection >- graphModel.removeConnection(this); >- this.curveDepth = depth; >- this.connectionFigure = createFigure(); >- registerConnection(sourceNode, destinationNode); >- updateFigure(this.connectionFigure); >- } else { >- this.curveDepth = depth; >- updateFigure(this.connectionFigure); >- } >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.mylar.zest.core.widgets.IGraphItem#getItemType() >- */ >- public int getItemType() { >- return CONNECTION; >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.mylar.zest.core.internal.graphmodel.GraphItem#setVisible(boolean) >- */ >- public void setVisible(boolean visible) { >- //graphModel.addRemoveFigure(this, visible); >- if (getSource().isVisible() && getDestination().isVisible() && visible) { >- this.getFigure().setVisible(visible); >- if (sourceContainerConnectionFigure != null) { >- sourceContainerConnectionFigure.setVisible(visible); >- } >- if (targetContainerConnectionFigure != null) { >- targetContainerConnectionFigure.setVisible(visible); >- } >- this.visible = visible; >- } else { >- this.getFigure().setVisible(false); >- if (sourceContainerConnectionFigure != null) { >- sourceContainerConnectionFigure.setVisible(false); >- } >- if (targetContainerConnectionFigure != null) { >- targetContainerConnectionFigure.setVisible(false); >- } >- this.visible = false; >- } >- >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.mylar.zest.core.widgets.IGraphItem#isVisible() >- */ >- public boolean isVisible() { >- return visible; >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.swt.widgets.Item#setText(java.lang.String) >- */ >- public void setText(String string) { >- super.setText(string); >- >- if (this.connectionFigure != null) { >- updateFigure(this.connectionFigure); >- } >- } >- >- PolylineConnection getSourceContainerConnectionFigure() { >- return (PolylineConnection) sourceContainerConnectionFigure; >- } >- >- PolylineConnection getTargetContainerConnectionFigure() { >- return (PolylineConnection) targetContainerConnectionFigure; >- } >- >- private void updateFigure(Connection connection) { >- if (sourceContainerConnectionFigure != null) { >- doUpdateFigure(sourceContainerConnectionFigure); >- } >- if (targetContainerConnectionFigure != null) { >- doUpdateFigure(targetContainerConnectionFigure); >- } >- doUpdateFigure(connection); >- } >- >- private void doUpdateFigure(Connection connection) { >- if (connection == null) { >- return; >- } >- Shape connectionShape = (Shape) connection; >- >- connectionShape.setLineStyle(getLineStyle()); >- >- if (this.getText() != null || this.getImage() != null) { >- //Label l = new Label(this.getText(), this.getImage()); >- if (this.getImage() != null) { >- this.connectionLabel.setIcon(this.getImage()); >- } >- if (this.getText() != null) { >- this.connectionLabel.setText(this.getText()); >- } >- this.connectionLabel.setFont(this.getFont()); >- } >- >- if (highlighted) { >- connectionShape.setForegroundColor(getHighlightColor()); >- connectionShape.setLineWidth(getLineWidth() * 2); >- } else { >- connectionShape.setForegroundColor(getLineColor()); >- connectionShape.setLineWidth(getLineWidth()); >- } >- >- if (connection instanceof PolylineArcConnection) { >- PolylineArcConnection arcConnection = (PolylineArcConnection) connection; >- arcConnection.setDepth(curveDepth); >- } >- if ((connectionStyle & ZestStyles.CONNECTIONS_DIRECTED) > 0) { >- PolygonDecoration decoration = new PolygonDecoration(); >- if (getLineWidth() < 3) { >- decoration.setScale(9, 3); >- } else { >- double logLineWith = getLineWidth() / 2.0; >- decoration.setScale(7 * logLineWith, 3 * logLineWith); >- } >- ((PolylineConnection) connection).setTargetDecoration(decoration); >- } >- >- IFigure toolTip; >- if (this.getTooltip() == null && getText() != null && getText().length() > 0 && hasCustomTooltip == false) { >- toolTip = new Label(); >- ((Label) toolTip).setText(getText()); >- } else { >- toolTip = this.getTooltip(); >- } >- connection.setToolTip(toolTip); >- } >- >- private Connection createFigure() { >- /* >- if ((sourceNode.getParent()).getItemType() == GraphItem.CONTAINER) { >- GraphContainer container = (GraphContainer) sourceNode.getParent(); >- sourceContainerConnectionFigure = doCreateFigure(); >- container.addConnectionFigure((PolylineConnection) sourceContainerConnectionFigure); >- } >- if ((destinationNode.getParent()).getItemType() == GraphItem.CONTAINER) { >- GraphContainer container = (GraphContainer) destinationNode.getParent(); >- targetContainerConnectionFigure = doCreateFigure(); >- container.addConnectionFigure((PolylineConnection) targetContainerConnectionFigure); >- } >- */ >- >- return doCreateFigure(); >- >- } >- >- private Connection doCreateFigure() { >- Connection connectionFigure = null; >- ChopboxAnchor sourceAnchor = null; >- ChopboxAnchor targetAnchor = null; >- this.connectionLabel = new Label(); >- Locator labelLocator = null; >- >- if (getSource() == getDestination()) { >- // If this is a self loop, create a looped arc and put the locator at the top >- // of the connection >- connectionFigure = new PolylineArcConnection(); >- sourceAnchor = new LoopAnchor(getSource().getNodeFigure()); >- targetAnchor = new LoopAnchor(getDestination().getNodeFigure()); >- labelLocator = new MidpointLocator(connectionFigure, 0) { >- protected Point getReferencePoint() { >- Point p = Point.SINGLETON; >- p.x = getConnection().getPoints().getPoint(getIndex()).x; >- p.y = (int) (getConnection().getPoints().getPoint(getIndex()).y - (curveDepth * 1.5)); >- return p; >- } >- }; >- } else { >- if (curveDepth != 0) { >- connectionFigure = new PolylineArcConnection(); >- ((PolylineArcConnection) connectionFigure).setDepth(this.curveDepth); >- } else { >- connectionFigure = new PolylineConnection(); >- } >- sourceAnchor = new RoundedChopboxAnchor(getSource().getNodeFigure(), 8); >- targetAnchor = new RoundedChopboxAnchor(getDestination().getNodeFigure(), 8); >- labelLocator = new MidpointLocator(connectionFigure, 0); >- } >- >- connectionFigure.setSourceAnchor(sourceAnchor); >- connectionFigure.setTargetAnchor(targetAnchor); >- connectionFigure.add(this.connectionLabel, labelLocator); >- >- doUpdateFigure(connectionFigure); >- return connectionFigure; >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.mylar.zest.layouts.LayoutRelationship#isBidirectionalInLayout() >- */ >- private boolean isBidirectionalInLayout() { >- return !ZestStyles.checkStyle(connectionStyle, ZestStyles.CONNECTIONS_DIRECTED); >- } >- >- class GraphLayoutConnection implements LayoutRelationship { >- >- Object layoutInformation = null; >- >- public void clearBendPoints() { >- // @tag TODO : add bendpoints >- } >- >- public LayoutEntity getDestinationInLayout() { >- return getDestination().getLayoutEntity(); >- } >- >- public Object getLayoutInformation() { >- return layoutInformation; >- } >- >- public LayoutEntity getSourceInLayout() { >- return getSource().getLayoutEntity(); >- } >- >- public void populateLayoutConstraint(LayoutConstraint constraint) { >- graphModel.invokeConstraintAdapters(GraphConnection.this, constraint); >- } >- >- public void setBendPoints(LayoutBendPoint[] bendPoints) { >- // @tag TODO : add bendpoints >- } >- >- public void setLayoutInformation(Object layoutInformation) { >- this.layoutInformation = layoutInformation; >- } >- >- public Object getGraphData() { >- return GraphConnection.this; >- } >- >- public void setGraphData(Object o) { >- >- } >- >- } >- >- IFigure getFigure() { >- return this.getConnectionFigure(); >- } >-} >+/******************************************************************************* >+ * Copyright 2005-2010, 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 >+ * Mateusz Matela >+ ******************************************************************************/ >+package org.eclipse.zest.core.widgets; >+ >+import org.eclipse.draw2d.ChopboxAnchor; >+import org.eclipse.draw2d.ColorConstants; >+import org.eclipse.draw2d.Connection; >+import org.eclipse.draw2d.Graphics; >+import org.eclipse.draw2d.IFigure; >+import org.eclipse.draw2d.Label; >+import org.eclipse.draw2d.Locator; >+import org.eclipse.draw2d.MidpointLocator; >+import org.eclipse.draw2d.PolygonDecoration; >+import org.eclipse.draw2d.PolylineConnection; >+import org.eclipse.draw2d.Shape; >+import org.eclipse.draw2d.geometry.Point; >+import org.eclipse.swt.graphics.Color; >+import org.eclipse.swt.graphics.Font; >+import org.eclipse.swt.widgets.Display; >+import org.eclipse.zest.core.widgets.internal.LoopAnchor; >+import org.eclipse.zest.core.widgets.internal.PolylineArcConnection; >+import org.eclipse.zest.core.widgets.internal.RoundedChopboxAnchor; >+import org.eclipse.zest.core.widgets.internal.ZestRootLayer; >+import org.eclipse.zest.layouts.interfaces.ConnectionLayout; >+import org.eclipse.zest.layouts.interfaces.NodeLayout; >+ >+/* >+ * This is the graph connection model which stores the source and destination >+ * nodes and the properties of this connection (color, line width etc). >+ * >+ * @author Chris Callendar >+ * >+ * @author Ian Bull >+ */ >+public class GraphConnection extends GraphItem { >+ >+ private Font font; >+ private GraphNode sourceNode; >+ private GraphNode destinationNode; >+ >+ private double weight; >+ private Color color; >+ private Color highlightColor; >+ private Color foreground; >+ private int lineWidth; >+ private int lineStyle; >+ private final Graph graph; >+ >+ private int connectionStyle; >+ private int curveDepth; >+ private boolean isDisposed = false; >+ >+ private Label connectionLabel = null; >+ private Connection connectionFigure = null; >+ private Connection sourceContainerConnectionFigure = null; >+ private Connection targetContainerConnectionFigure = null; >+ >+ /** >+ * The state of visibility set by the user. >+ */ >+ private boolean visible; >+ >+ private IFigure tooltip; >+ >+ private boolean highlighted; >+ private boolean hasCustomTooltip; >+ >+ public GraphConnection(Graph graphModel, int style, GraphNode source, >+ GraphNode destination) { >+ super(graphModel, style); >+ >+ this.connectionStyle |= graphModel.getConnectionStyle(); >+ this.connectionStyle |= style; >+ this.sourceNode = source; >+ this.destinationNode = destination; >+ this.visible = true; >+ this.color = ColorConstants.lightGray; >+ this.foreground = ColorConstants.lightGray; >+ this.highlightColor = graphModel.DARK_BLUE; >+ this.lineWidth = 1; >+ this.lineStyle = Graphics.LINE_SOLID; >+ setWeight(1.0); >+ this.graph = graphModel; >+ this.curveDepth = 0; >+ this.font = Display.getDefault().getSystemFont(); >+ registerConnection(source, destination); >+ } >+ >+ private void registerConnection(GraphNode source, GraphNode destination) { >+ if (source.getSourceConnections().contains(this)) { >+ source.removeSourceConnection(this); >+ } >+ if (destination.getTargetConnections().contains(this)) { >+ destination.removeTargetConnection(this); >+ } >+ (source).addSourceConnection(this); >+ (destination).addTargetConnection(this); >+ >+ if (source.getParent().getItemType() == GraphItem.CONTAINER >+ && destination.getParent().getItemType() == GraphItem.CONTAINER >+ && (source.getParent() == destination.getParent())) { >+ // 196189: Edges should not draw on the edge layer if both the src >+ // and dest are in the same container >+ // https://bugs.eclipse.org/bugs/show_bug.cgi?id=196189 >+ graph.addConnection(this, false); >+ } else { >+ graph.addConnection(this, true); >+ } >+ >+ if ((source.getParent()).getItemType() == GraphItem.CONTAINER) { >+ // If the container of the source is a container, we need to draw >+ // another >+ // arc on that arc layer >+ sourceContainerConnectionFigure = doCreateFigure(); >+ ((GraphContainer) source.getParent()) >+ .addConnectionFigure(sourceContainerConnectionFigure); >+ this.setVisible(false); >+ } >+ >+ if ((destination.getParent()).getItemType() == GraphItem.CONTAINER) { // && >+ // src_destSameContainer >+ // == >+ // false) >+ // { >+ // If the container of the source is a container, we need to draw >+ // another >+ // arc on that arc layer >+ targetContainerConnectionFigure = doCreateFigure(); >+ ((GraphContainer) destination.getParent()) >+ .addConnectionFigure(targetContainerConnectionFigure); >+ this.setVisible(false); >+ } >+ graph.registerItem(this); >+ } >+ >+ void removeFigure() { >+ if (connectionFigure.getParent() != null) { >+ connectionFigure.getParent().remove(connectionFigure); >+ } >+ connectionFigure = null; >+ if (sourceContainerConnectionFigure != null) { >+ sourceContainerConnectionFigure.getParent().remove( >+ sourceContainerConnectionFigure); >+ sourceContainerConnectionFigure = null; >+ } >+ if (targetContainerConnectionFigure != null) { >+ targetContainerConnectionFigure.getParent().remove( >+ targetContainerConnectionFigure); >+ targetContainerConnectionFigure = null; >+ } >+ >+ } >+ >+ public void dispose() { >+ super.dispose(); >+ this.isDisposed = true; >+ (getSource()).removeSourceConnection(this); >+ (getDestination()).removeTargetConnection(this); >+ graph.removeConnection(this); >+ if (sourceContainerConnectionFigure != null) { >+ sourceContainerConnectionFigure.getParent().remove( >+ sourceContainerConnectionFigure); >+ } >+ if (targetContainerConnectionFigure != null) { >+ targetContainerConnectionFigure.getParent().remove( >+ targetContainerConnectionFigure); >+ } >+ } >+ >+ public boolean isDisposed() { >+ return isDisposed; >+ } >+ >+ public Connection getConnectionFigure() { >+ if (connectionFigure == null) { >+ connectionFigure = doCreateFigure(); >+ } >+ return connectionFigure; >+ } >+ >+ /** >+ * Gets the external connection object. >+ * >+ * @return Object >+ */ >+ public Object getExternalConnection() { >+ return this.getData(); >+ } >+ >+ /** >+ * Returns a string like 'source -> destination' >+ * >+ * @return String >+ */ >+ public String toString() { >+ StringBuffer buffer = new StringBuffer("GraphModelConnection: "); >+ buffer.append(sourceNode != null ? sourceNode.getText() : "null"); >+ buffer.append(isDirected() ? " --> " : " --- "); >+ buffer.append(destinationNode != null ? destinationNode.getText() >+ : "null"); >+ buffer.append(" (weight=").append(getWeightInLayout()).append(")"); >+ return buffer.toString(); >+ } >+ >+ /** >+ * Returns the style of this connection. Valid styles are those that begin >+ * with CONNECTION in ZestStyles. >+ * >+ * @return the style of this connection. >+ * @see #ZestStyles >+ */ >+ public int getConnectionStyle() { >+ return connectionStyle; >+ } >+ >+ /** >+ * Returns the style of this connection. Valid styles are those that begin >+ * with CONNECTION in ZestStyles. >+ * >+ * @return the style of this connection. >+ * @see #ZestStyles >+ */ >+ public void setConnectionStyle(int style) { >+ this.connectionStyle = style; >+ updateFigure(this.connectionFigure); >+ } >+ >+ /** >+ * Gets the weight of this connection. The weight must be in {-1, [0-1]}. A >+ * weight of -1 means that there is no force/tension between the nodes. A >+ * weight of 0 results in the maximum spring length being used (farthest >+ * apart). A weight of 1 results in the minimum spring length being used >+ * (closest together). >+ * >+ * @see org.eclipse.mylar.zest.layouts.LayoutRelationship#getWeightInLayout() >+ * @return the weight: {-1, [0 - 1]}. >+ */ >+ public double getWeightInLayout() { >+ return weight; >+ } >+ >+ /** >+ * Gets the font for the label on this connection >+ * >+ * @return >+ */ >+ public Font getFont() { >+ return this.font; >+ } >+ >+ /** >+ * Sets the font for the label on this connection. >+ * >+ */ >+ public void setFont(Font f) { >+ this.font = f; >+ } >+ >+ /** >+ * Sets the weight for this connection. The weight must be in {-1, [0-1]}. A >+ * weight of -1 means that there is no force/tension between the nodes. A >+ * weight of 0 results in the maximum spring length being used (farthest >+ * apart). A weight of 1 results in the minimum spring length being used >+ * (closest together). >+ * >+ */ >+ public void setWeight(double weight) { >+ if (weight < 0) { >+ this.weight = -1; >+ } else if (weight > 1) { >+ this.weight = 1; >+ } else { >+ this.weight = weight; >+ } >+ } >+ >+ /** >+ * Returns the color of this connection. >+ * >+ * @return Color >+ */ >+ public Color getLineColor() { >+ return color; >+ } >+ >+ /** >+ * Sets the highlight color. >+ * >+ * @param color >+ * the color to use for highlighting. >+ */ >+ public void setHighlightColor(Color color) { >+ this.highlightColor = color; >+ } >+ >+ /** >+ * @return the highlight color >+ */ >+ public Color getHighlightColor() { >+ return highlightColor; >+ } >+ >+ /** >+ * Perminently sets the color of this line to the given color. This will >+ * become the color of the line when it is not highlighted. If you would >+ * like to temporarily change the color of the line, use changeLineColor. >+ * >+ * @param color >+ * the color to be set. >+ * @see changeLineColor(Color color) >+ */ >+ public void setLineColor(Color color) { >+ this.foreground = color; >+ changeLineColor(foreground); >+ } >+ >+ /** >+ * Sets the connection color. >+ * >+ * @param color >+ */ >+ public void changeLineColor(Color color) { >+ this.color = color; >+ updateFigure(connectionFigure); >+ } >+ >+ /** >+ * Sets the tooltip on this node. This tooltip will display if the mouse >+ * hovers over the node. Setting the tooltip has no effect if a custom >+ * figure has been set. >+ */ >+ public void setTooltip(IFigure tooltip) { >+ hasCustomTooltip = true; >+ this.tooltip = tooltip; >+ updateFigure(connectionFigure); >+ } >+ >+ /** >+ * Gets the current tooltip for this node. The tooltip returned is >+ * meaningless if a custom figure has been set. >+ */ >+ public IFigure getTooltip() { >+ return this.tooltip; >+ } >+ >+ /** >+ * Returns the connection line width. >+ * >+ * @return int >+ */ >+ public int getLineWidth() { >+ return lineWidth; >+ } >+ >+ /** >+ * Sets the connection line width. >+ * >+ * @param lineWidth >+ */ >+ public void setLineWidth(int lineWidth) { >+ this.lineWidth = lineWidth; >+ updateFigure(connectionFigure); >+ } >+ >+ /** >+ * Returns the connection line style. >+ * >+ * @return int >+ */ >+ public int getLineStyle() { >+ return lineStyle; >+ } >+ >+ /** >+ * Sets the connection line style. >+ * >+ * @param lineStyle >+ */ >+ public void setLineStyle(int lineStyle) { >+ this.lineStyle = lineStyle; >+ updateFigure(connectionFigure); >+ } >+ >+ /** >+ * Gets the source node for this relationship >+ * >+ * @return GraphModelNode >+ */ >+ public GraphNode getSource() { >+ return this.sourceNode; >+ } >+ >+ /** >+ * Gets the target node for this relationship >+ * >+ * @return GraphModelNode >+ */ >+ public GraphNode getDestination() { >+ return this.destinationNode; >+ } >+ >+ /** >+ * Highlights this node. Uses the default highlight color. >+ */ >+ public void highlight() { >+ if (highlighted) { >+ return; >+ } >+ IFigure parentFigure = connectionFigure.getParent(); >+ if (parentFigure instanceof ZestRootLayer) { >+ ((ZestRootLayer) parentFigure) >+ .highlightConnection(connectionFigure); >+ } >+ highlighted = true; >+ updateFigure(connectionFigure); >+ } >+ >+ /** >+ * Unhighlights this node. Uses the default color. >+ */ >+ public void unhighlight() { >+ if (!highlighted) { >+ return; >+ } >+ IFigure parentFigure = connectionFigure.getParent(); >+ if (parentFigure instanceof ZestRootLayer) { >+ ((ZestRootLayer) parentFigure) >+ .unHighlightConnection(connectionFigure); >+ } >+ highlighted = false; >+ updateFigure(connectionFigure); >+ } >+ >+ /** >+ * Returns true if this connection is highlighted, false otherwise >+ * >+ * @return >+ */ >+ public boolean isHighlighted() { >+ return highlighted; >+ } >+ >+ /** >+ * Gets the graph model that this connection is in >+ * >+ * @return The graph model that this connection is contained in >+ */ >+ public Graph getGraphModel() { >+ return this.graph; >+ } >+ >+ /** >+ * Sets the curve depth of the arc. The curve depth is defined as the >+ * maximum distance from any point on the chord (i.e. a vector normal to the >+ * chord with magnitude d). >+ * >+ * If 0 is set, a Polyline Connection will be used, otherwise a >+ * PolylineArcConnectoin will be used. Negative depths are also supported. >+ * >+ * @param depth >+ * The depth of the curve >+ */ >+ public void setCurveDepth(int depth) { >+ if (this.curveDepth == 0 && depth != 0 || this.curveDepth != 0 >+ && depth == 0) { >+ // There is currently no curve, so we have to create >+ // a curved connection >+ graph.removeConnection(this); >+ this.curveDepth = depth; >+ this.connectionFigure = doCreateFigure(); >+ registerConnection(sourceNode, destinationNode); >+ updateFigure(this.connectionFigure); >+ } else { >+ this.curveDepth = depth; >+ updateFigure(this.connectionFigure); >+ } >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see org.eclipse.mylar.zest.core.widgets.IGraphItem#getItemType() >+ */ >+ public int getItemType() { >+ return CONNECTION; >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see >+ * org.eclipse.mylar.zest.core.internal.graphmodel.GraphItem#setVisible( >+ * boolean) >+ */ >+ public void setVisible(boolean visible) { >+ if (getSource().isVisible() && getDestination().isVisible() && visible) { >+ this.getFigure().setVisible(visible); >+ if (sourceContainerConnectionFigure != null) { >+ sourceContainerConnectionFigure.setVisible(visible); >+ } >+ if (targetContainerConnectionFigure != null) { >+ targetContainerConnectionFigure.setVisible(visible); >+ } >+ this.visible = visible; >+ } else { >+ this.getFigure().setVisible(false); >+ if (sourceContainerConnectionFigure != null) { >+ sourceContainerConnectionFigure.setVisible(false); >+ } >+ if (targetContainerConnectionFigure != null) { >+ targetContainerConnectionFigure.setVisible(false); >+ } >+ this.visible = false; >+ } >+ >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see org.eclipse.mylar.zest.core.widgets.IGraphItem#isVisible() >+ */ >+ public boolean isVisible() { >+ return visible; >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see org.eclipse.swt.widgets.Item#setText(java.lang.String) >+ */ >+ public void setText(String string) { >+ super.setText(string); >+ >+ if (this.connectionFigure != null) { >+ updateFigure(this.connectionFigure); >+ } >+ } >+ >+ /** >+ * @since 2.0 >+ */ >+ public boolean isDirected() { >+ return ZestStyles.checkStyle(connectionStyle, >+ ZestStyles.CONNECTIONS_DIRECTED); >+ } >+ >+ /** >+ * @since 2.0 >+ */ >+ public void setDirected(boolean directed) { >+ if (directed) { >+ setConnectionStyle(connectionStyle >+ | ZestStyles.CONNECTIONS_DIRECTED); >+ } else { >+ setConnectionStyle(connectionStyle >+ & (-1 - ZestStyles.CONNECTIONS_DIRECTED)); >+ } >+ } >+ >+ PolylineConnection getSourceContainerConnectionFigure() { >+ return (PolylineConnection) sourceContainerConnectionFigure; >+ } >+ >+ PolylineConnection getTargetContainerConnectionFigure() { >+ return (PolylineConnection) targetContainerConnectionFigure; >+ } >+ >+ private void updateFigure(Connection connection) { >+ if (sourceContainerConnectionFigure != null) { >+ doUpdateFigure(sourceContainerConnectionFigure); >+ } >+ if (targetContainerConnectionFigure != null) { >+ doUpdateFigure(targetContainerConnectionFigure); >+ } >+ doUpdateFigure(connection); >+ } >+ >+ private void doUpdateFigure(Connection connection) { >+ if (connection == null) { >+ return; >+ } >+ Shape connectionShape = (Shape) connection; >+ >+ connectionShape.setLineStyle(getLineStyle()); >+ >+ if (this.getText() != null || this.getImage() != null) { >+ if (this.getImage() != null) { >+ this.connectionLabel.setIcon(this.getImage()); >+ } >+ if (this.getText() != null) { >+ this.connectionLabel.setText(this.getText()); >+ } >+ this.connectionLabel.setFont(this.getFont()); >+ } >+ >+ if (highlighted) { >+ connectionShape.setForegroundColor(getHighlightColor()); >+ connectionShape.setLineWidth(getLineWidth() * 2); >+ } else { >+ connectionShape.setForegroundColor(getLineColor()); >+ connectionShape.setLineWidth(getLineWidth()); >+ } >+ >+ if (connection instanceof PolylineArcConnection) { >+ PolylineArcConnection arcConnection = (PolylineArcConnection) connection; >+ arcConnection.setDepth(curveDepth); >+ } >+ if ((connectionStyle & ZestStyles.CONNECTIONS_DIRECTED) > 0) { >+ PolygonDecoration decoration = new PolygonDecoration(); >+ if (getLineWidth() < 3) { >+ decoration.setScale(9, 3); >+ } else { >+ double logLineWith = getLineWidth() / 2.0; >+ decoration.setScale(7 * logLineWith, 3 * logLineWith); >+ } >+ ((PolylineConnection) connection).setTargetDecoration(decoration); >+ } >+ >+ IFigure toolTip; >+ if (this.getTooltip() == null && getText() != null >+ && getText().length() > 0 && hasCustomTooltip == false) { >+ toolTip = new Label(); >+ ((Label) toolTip).setText(getText()); >+ } else { >+ toolTip = this.getTooltip(); >+ } >+ connection.setToolTip(toolTip); >+ } >+ >+ private Connection doCreateFigure() { >+ Connection connectionFigure = null; >+ ChopboxAnchor sourceAnchor = null; >+ ChopboxAnchor targetAnchor = null; >+ this.connectionLabel = new Label(); >+ Locator labelLocator = null; >+ >+ if (getSource() == getDestination()) { >+ // If this is a self loop, create a looped arc and put the locator >+ // at the top >+ // of the connection >+ connectionFigure = new PolylineArcConnection(); >+ sourceAnchor = new LoopAnchor(getSource().getNodeFigure()); >+ targetAnchor = new LoopAnchor(getDestination().getNodeFigure()); >+ labelLocator = new MidpointLocator(connectionFigure, 0) { >+ protected Point getReferencePoint() { >+ Point p = Point.SINGLETON; >+ p.x = getConnection().getPoints().getPoint(getIndex()).x; >+ p.y = (int) (getConnection().getPoints().getPoint( >+ getIndex()).y - (curveDepth * 1.5)); >+ return p; >+ } >+ }; >+ } else { >+ if (curveDepth != 0) { >+ connectionFigure = new PolylineArcConnection(); >+ ((PolylineArcConnection) connectionFigure) >+ .setDepth(this.curveDepth); >+ } else { >+ connectionFigure = new PolylineConnection(); >+ } >+ sourceAnchor = new RoundedChopboxAnchor( >+ getSource().getNodeFigure(), 8); >+ targetAnchor = new RoundedChopboxAnchor(getDestination() >+ .getNodeFigure(), 8); >+ labelLocator = new MidpointLocator(connectionFigure, 0); >+ } >+ >+ connectionFigure.setSourceAnchor(sourceAnchor); >+ connectionFigure.setTargetAnchor(targetAnchor); >+ connectionFigure.add(this.connectionLabel, labelLocator); >+ >+ doUpdateFigure(connectionFigure); >+ return connectionFigure; >+ } >+ >+ IFigure getFigure() { >+ return this.getConnectionFigure(); >+ } >+ >+ private InternalConnectionLayout layout; >+ >+ InternalConnectionLayout getLayout() { >+ if (layout == null) { >+ layout = new InternalConnectionLayout(); >+ } >+ return layout; >+ } >+ >+ class InternalConnectionLayout implements ConnectionLayout { >+ private boolean visible = GraphConnection.this.isVisible(); >+ >+ public NodeLayout getSource() { >+ return sourceNode.getLayout(); >+ } >+ >+ public NodeLayout getTarget() { >+ return destinationNode.getLayout(); >+ } >+ >+ public double getWeight() { >+ return GraphConnection.this.getWeightInLayout(); >+ } >+ >+ public boolean isDirected() { >+ return !ZestStyles.checkStyle(getConnectionStyle(), >+ ZestStyles.CONNECTIONS_DIRECTED); >+ } >+ >+ public boolean isVisible() { >+ return visible; >+ } >+ >+ public void setVisible(boolean visible) { >+ graph.getLayoutContext().checkChangesAllowed(); >+ this.visible = visible; >+ } >+ >+ void applyLayout() { >+ if (GraphConnection.this.isVisible() != this.visible) { >+ GraphConnection.this.setVisible(this.visible); >+ } >+ } >+ } >+ >+ void applyLayoutChanges() { >+ if (layout != null) { >+ layout.applyLayout(); >+ } >+ } >+} >Index: src/org/eclipse/zest/core/widgets/GraphContainer.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/widgets/GraphContainer.java,v >retrieving revision 1.25 >diff -u -r1.25 GraphContainer.java >--- src/org/eclipse/zest/core/widgets/GraphContainer.java 31 Mar 2009 16:39:23 -0000 1.25 >+++ src/org/eclipse/zest/core/widgets/GraphContainer.java 15 Mar 2010 03:48:58 -0000 >@@ -1,835 +1,947 @@ >-/******************************************************************************* >- * Copyright 2005-2007, 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.widgets; >- >-import java.util.ArrayList; >-import java.util.Iterator; >-import java.util.LinkedList; >-import java.util.List; >- >-import org.eclipse.draw2d.Animation; >-import org.eclipse.draw2d.ColorConstants; >-import org.eclipse.draw2d.FreeformLayout; >-import org.eclipse.draw2d.FreeformViewport; >-import org.eclipse.draw2d.IFigure; >-import org.eclipse.draw2d.LayoutAnimator; >-import org.eclipse.draw2d.LineBorder; >-import org.eclipse.draw2d.PolylineConnection; >-import org.eclipse.draw2d.ScrollPane; >-import org.eclipse.draw2d.Viewport; >-import org.eclipse.draw2d.geometry.Dimension; >-import org.eclipse.draw2d.geometry.Point; >-import org.eclipse.draw2d.geometry.Rectangle; >-import org.eclipse.swt.graphics.Image; >-import org.eclipse.zest.core.widgets.internal.AspectRatioFreeformLayer; >-import org.eclipse.zest.core.widgets.internal.ContainerFigure; >-import org.eclipse.zest.core.widgets.internal.ExpandGraphLabel; >-import org.eclipse.zest.core.widgets.internal.ZestRootLayer; >-import org.eclipse.zest.layouts.InvalidLayoutConfiguration; >-import org.eclipse.zest.layouts.LayoutAlgorithm; >-import org.eclipse.zest.layouts.LayoutEntity; >-import org.eclipse.zest.layouts.LayoutRelationship; >-import org.eclipse.zest.layouts.LayoutStyles; >-import org.eclipse.zest.layouts.algorithms.TreeLayoutAlgorithm; >- >-/* >- * A Container than can be added to a Graph. Nodes can be added to this >- * container. The container supports collapsing and expanding and has the same >- * properties as the nodes. Containers cannot have custom figures. >- * >- * @author Ian Bull >- */ >-public class GraphContainer extends GraphNode implements IContainer { >- >- //private static final double CONTAINER_SCALE = 0.75; >- private static final double scaledWidth = 300; >- private static final double scaledHeight = 200; >- private static final int CONTAINER_HEIGHT = 200; >- private static final int MIN_WIDTH = 250; >- private static final int ANIMATION_TIME = 100; >- private static final int SUBLAYER_OFFSET = 2; >- >- private ExpandGraphLabel expandGraphLabel; >- >- //private FreeformLayer container; >- //private FreeformLayer edgeLayer; >- private List childNodes = null; >- private int childAreaHeight = CONTAINER_HEIGHT; >- >- public ZestRootLayer zestLayer; >- private ScrollPane scrollPane; >- private LayoutAlgorithm layoutAlgorithm; >- private boolean isExpanded = false; >- //private ScalableFreeformLayeredPane scalledLayer; >- private AspectRatioFreeformLayer scalledLayer; >- >- /** >- * Creates a new GraphContainer. A GraphContainer may contain nodes, >- * and has many of the same properties as a graph node. >- * @param graph The graph that the container is being added to >- * @param style >- */ >- public GraphContainer(IContainer graph, int style) { >- this(graph, style, ""); >- >- } >- >- public GraphContainer(IContainer graph, int style, String text) { >- this(graph, style, text, null); >- >- } >- >- public GraphContainer(IContainer graph, int style, String text, Image image) { >- super(graph, style, text, image); >- initModel(graph, text, image); >- close(false); >- childNodes = new ArrayList(); >- } >- >- /** >- * Custom figures cannot be set on a GraphContainer. >- */ >- public void setCustomFigure(IFigure nodeFigure) { >- throw new RuntimeException("Operation not supported: Containers cannot have custom figures"); >- } >- >- /* >- * (non-Javadoc) >- * @see org.eclipse.mylar.zest.core.widgets.GraphItem#getItemType() >- public int getItemType() { >- return GraphItem.CONTAINER; >- } >- >- /** >- * Gets the figure for this container. >- */ >- public IFigure getNodeFigure() { >- return this.nodeFigure; >- } >- >- /** >- * Close this node. >- * @param animate >- */ >- public void close(boolean animate) { >- if (animate) { >- Animation.markBegin(); >- } >- isExpanded = false; >- >- expandGraphLabel.setExpandedState(ExpandGraphLabel.CLOSED); >- Rectangle newBounds = scrollPane.getBounds().getCopy(); >- newBounds.height = 0; >- >- //this.nodeFigure.setConstraint(scrollPane, newBounds); >- //this.nodeFigure.revalidate(); >- scrollPane.setSize(scrollPane.getSize().width, 0); >- updateFigureForModel(this.zestLayer); >- scrollPane.setVisible(false); >- //setSize(expandGraphLabel.getSize().width, expandGraphLabel.getSize().height); >- List children = this.zestLayer.getChildren(); >- for (Iterator iterator = children.iterator(); iterator.hasNext();) { >- IFigure child = (IFigure) iterator.next(); >- GraphItem item = getGraph().getGraphItem(child); >- item.setVisible(false); >- } >- Rectangle containerBounds = new Rectangle(this.getLocation(), new Dimension(this.getSize().width, CONTAINER_HEIGHT + this.expandGraphLabel.getSize().height)); >- moveNodesUp(containerBounds, this); >- if (animate) { >- Animation.run(ANIMATION_TIME); >- } >- //this.nodeFigure.getUpdateManager().performUpdate(); >- updateFigureForModel(nodeFigure); >- } >- >- private static void addNodeToOrderedList(List orderedNodeList, GraphNode node) { >- Iterator orderedNodeIterator = orderedNodeList.iterator(); >- int counter = 0; >- while (orderedNodeIterator.hasNext()) { >- // Look through the list of nodes below and find the right spot for this >- GraphNode nextOrderedNode = (GraphNode) orderedNodeIterator.next(); >- if (nextOrderedNode.getLocation().y + nextOrderedNode.getBounds().height > node.getLocation().y + node.getBounds().height) { >- break; >- } >- counter++; >- } >- // Place this in the right location >- orderedNodeList.add(counter, node); >- } >- >- /** >- * Gets all the nodes below the yValue. The nodes are returned in order. >- * @param nodes >- * @param yValue >- * @return >- */ >- private static List getOrderedNodesBelowY(List nodes, int yValue, GraphNode yValueNode) { >- Iterator iterator = nodes.iterator(); >- LinkedList orderedNode = new LinkedList(); >- while (iterator.hasNext()) { >- GraphNode node = (GraphNode) iterator.next(); >- if (node == yValueNode) { >- continue; >- } >- if (node.getLocation().y + node.getBounds().height > yValue) { >- // This node is below the container >- addNodeToOrderedList(orderedNode, node); >- } >- } >- // Convert this to an arrayList for faster access >- List arrayList = new ArrayList(); >- iterator = orderedNode.iterator(); >- while (iterator.hasNext()) { >- arrayList.add(iterator.next()); >- } >- return arrayList; >- } >- >- /** >- * Checks if the node intersects the stripe between left and right >- * @param left >- * @param right >- * @param node >- * @return >- */ >- private static boolean nodeInStripe(int left, int right, GraphNode node) { >- return (node.getBounds().x < right && node.getBounds().x + node.getBounds().width > left); >- } >- >- void pack(Graph g) { >- GraphNode highestNode = getHighestNode(g); >- moveNodesUp(highestNode.getBounds(), highestNode); >- } >- >- /** >- * >- * @param g >- * @return >- */ >- static GraphNode getHighestNode(Graph g) { >- Iterator iterator = g.getNodes().iterator(); >- GraphNode lowest /* highest on the screen */= null; >- >- while (iterator.hasNext()) { >- GraphNode node = (GraphNode) iterator.next(); >- if (lowest == null || lowest.getBounds().y > node.getBounds().y) { >- lowest = node; >- } >- } >- return lowest; >- >- } >- >- /** >- * Move the nodes below this node up >- * @param containerBounds >- * @param graphContainer >- */ >- private void moveNodesUp(Rectangle containerBounds, GraphNode graphContainer) { >- >- // Get all nodes below this container, in order >- List orderedNodesBelowY = getOrderedNodesBelowY(parent.getNodes(), containerBounds.y, graphContainer); >- int leftSide = containerBounds.x; >- int rightSide = containerBounds.x + containerBounds.width; >- List nodesToConsider = new LinkedList(); >- for (int i = 0; i < orderedNodesBelowY.size(); i++) { >- nodesToConsider.add(orderedNodesBelowY.get(i)); >- } >- addNodeToOrderedList(orderedNodesBelowY, graphContainer); >- >- while (nodesToConsider.size() > 0) { >- GraphNode node = (GraphNode) nodesToConsider.get(0); >- if (nodeInStripe(leftSide, rightSide, node)) { >- leftSide = Math.min(leftSide, node.getBounds().x); >- rightSide = Math.max(rightSide, node.getBounds().x + node.getBounds().width); >- // If this node is in the stripe, move it up >- // the previous node >- GraphNode previousNode = null; >- int i = 0; >- for (; i < orderedNodesBelowY.size(); i++) { >- if (orderedNodesBelowY.get(i) == node) { >- break; >- } >- } >- int j = i - 1; >- while (j >= 0) { >- GraphNode pastNode = (GraphNode) orderedNodesBelowY.get(j); >- //if (nodeInStripe(leftSide, rightSide, pastNode)) { >- if (nodeInStripe(node.getBounds().x, node.getBounds().x + node.getBounds().width, pastNode)) { >- previousNode = pastNode; >- break; >- } >- j--; >- } >- if (previousNode == null) { >- previousNode = graphContainer; >- } >- int previousLocation = previousNode.getBounds().y + previousNode.getBounds().height + 2; >- >- orderedNodesBelowY.remove(i); >- node.setLocation(node.getLocation().x, previousLocation); >- addNodeToOrderedList(orderedNodesBelowY, node); >- >- } >- nodesToConsider.remove(node); >- } >- } >- >- /** >- * Open the container. This opens the graph container to >- * show the nodes within and update the twistie >- */ >- public void open(boolean animate) { >- if (animate) { >- Animation.markBegin(); >- } >- isExpanded = true; >- >- expandGraphLabel.setExpandedState(ExpandGraphLabel.OPEN); >- >- scrollPane.setSize(computeChildArea()); >- scrollPane.setVisible(true); >- //setSize(expandGraphLabel.getSize().width, expandGraphLabel.getSize().height + expandedHeight - SUBLAYER_OFFSET); >- >- List children = this.zestLayer.getChildren(); >- for (Iterator iterator = children.iterator(); iterator.hasNext();) { >- IFigure child = (IFigure) iterator.next(); >- GraphItem item = getGraph().getGraphItem(child); >- item.setVisible(true); >- } >- >- updateFigureForModel(nodeFigure); >- >- Rectangle containerBounds = new Rectangle(this.getLocation(), new Dimension(this.getSize().width, CONTAINER_HEIGHT + this.expandGraphLabel.getSize().height)); >- //moveIntersectedNodes(containerBounds, this); >- moveNodesDown(containerBounds, this); >- moveNodesUp(containerBounds, this); >- //pack(graph); >- if (animate) { >- Animation.run(ANIMATION_TIME); >- } >- this.getFigure().getUpdateManager().performValidation(); >- //this.nodeFigure.getUpdateManager().performUpdate(); >- >- } >- >- /** >- * >- * @param containerBounds >- * @param graphContainer >- */ >- private void moveNodesDown(Rectangle containerBounds, GraphContainer graphContainer) { >- >- // Find all nodes below here >- List nodesBelowHere = getOrderedNodesBelowY(parent.getNodes(), containerBounds.y, graphContainer); >- Iterator nodesBelowHereIterator = nodesBelowHere.iterator(); >- List nodesToMove = new LinkedList(); >- int left = containerBounds.x; >- int right = containerBounds.x + containerBounds.width; >- while (nodesBelowHereIterator.hasNext()) { >- GraphNode node = (GraphNode) nodesBelowHereIterator.next(); >- if (nodeInStripe(left, right, node)) { >- nodesToMove.add(node); >- left = Math.min(left, node.getBounds().x); >- right = Math.max(right, node.getBounds().x + node.getBounds().width); >- } >- } >- List intersectingNodes = intersectingNodes(containerBounds, nodesToMove, graphContainer); >- int delta = getMaxMovement(containerBounds, intersectingNodes); >- if (delta > 0) { >- shiftNodesDown(nodesToMove, delta); >- } >- >- } >- >- void highlightNode(GraphNode node) { >- >- } >- >- void highlightEdge(GraphConnection connection) { >- } >- >- void highlightNode(GraphContainer container) { >- >- } >- >- void unhighlightNode(GraphNode node) { >- >- } >- >- void unhighlightNode(GraphContainer container) { >- >- } >- >-// /** >-// * Gets a list of nodes below the given node >-// * @param node >-// * @return >-// */ >-// private List getNodesBelow(int y, List nodes) { >-// Iterator allNodes = nodes.iterator(); >-// LinkedList result = new LinkedList(); >-// while (allNodes.hasNext()) { >-// GraphNode nextNode = (GraphNode) allNodes.next(); >-// int top = nextNode.getLocation().y; >-// if (top > y) { >-// result.add(nextNode); >-// } >-// } >-// return result; >-// } >- >- /** >- * Checks all the nodes in the list of nodesToCheck to see if they intersect with the bounds set >- * @param node >- * @param nodesToCheck >- * @return >- */ >- private List intersectingNodes(Rectangle bounds, List nodesToCheck, GraphNode node) { >- List result = new LinkedList(); >- Iterator nodes = nodesToCheck.iterator(); >- while (nodes.hasNext()) { >- GraphNode nodeToCheck = (GraphNode) nodes.next(); >- if (node == nodeToCheck) { >- continue; >- } >- if (bounds.intersects(nodeToCheck.getBounds())) { >- result.add(nodeToCheck); >- } >- } >- return result; >- } >- >- /** >- * Gets the max distance the intersecting nodes need to be shifted to make room for the expanding node >- * @param bounds >- * @param nodesToMove >- * @return >- */ >- private int getMaxMovement(Rectangle bounds, List nodesToMove) { >- Iterator iterator = nodesToMove.iterator(); >- int maxMovement = 0; >- while (iterator.hasNext()) { >- GraphNode node = (GraphNode) iterator.next(); >- int yValue = node.getLocation().y; >- int distanceFromBottom = (bounds.y + bounds.height) - yValue; >- maxMovement = Math.max(maxMovement, distanceFromBottom); >- } >- return maxMovement + 3; >- } >- >- /** >- * Shifts a collection of nodes down. >- * @param nodesToShift >- * @param amount >- */ >- private void shiftNodesDown(List nodesToShift, int amount) { >- Iterator iterator = nodesToShift.iterator(); >- while (iterator.hasNext()) { >- GraphNode node = (GraphNode) iterator.next(); >- >- node.setLocation(node.getLocation().x, node.getLocation().y + amount); >- } >- } >- >-// /** >-// * This finds the highest Y Value of a set of nodes. >-// * @param nodes >-// * @return >-// */ >-// private int findSmallestYValue(List nodes) { >-// Iterator iterator = nodes.iterator(); >-// int lowestNode /*highest on the screen*/= Integer.MAX_VALUE - 100; // Subtract 100 so we don't overflow >-// while (iterator.hasNext()) { >-// GraphNode node = (GraphNode) iterator.next(); >-// int y = node.getLocation().y; >-// lowestNode = Math.min(lowestNode, y); >-// } >-// return lowestNode; >-// } >- >-// /** >-// * Clears the nodes that the container intersects as it expands >-// * @param containerBounds >-// * @param graphContainer >-// */ >-// private void moveIntersectedNodes(Rectangle containerBounds, GraphNode graphContainer) { >-// >-// List nodesBelowHere = getNodesBelow(this.getLocation().y, graphContainer.getGraphModel().getNodes()); >-// List intersectingNodes = intersectingNodes(containerBounds, nodesBelowHere, graphContainer); >-// int delta = getMaxMovement(containerBounds, intersectingNodes); >-// shiftNodesDown(intersectingNodes, delta); >-// >-// int lowestNode /*highest on the screen*/= findSmallestYValue(intersectingNodes); >-// nodesBelowHere = getNodesBelow(lowestNode, nodesBelowHere); >-// >-// while (nodesBelowHere.size() > 0) { >-// Iterator intersectingNodeIterator = intersectingNodes.iterator(); >-// List nodesMovedInLastIteration = new LinkedList(); >-// while (intersectingNodeIterator.hasNext()) { >-// GraphNode node = (GraphNode) intersectingNodeIterator.next(); >-// intersectingNodes = intersectingNodes(node.getBounds(), nodesBelowHere, node); >-// delta = getMaxMovement(node.getBounds(), intersectingNodes); >-// if (delta > 0) { >-// shiftNodesDown(intersectingNodes, delta); >-// nodesMovedInLastIteration.addAll(intersectingNodes); >-// } >-// } >-// lowestNode /*highest on the screen*/= findSmallestYValue(nodesMovedInLastIteration); >-// nodesBelowHere = getNodesBelow(lowestNode, nodesBelowHere); >-// intersectingNodes = nodesMovedInLastIteration; >-// } >-// } >- >- /** >- * Gets the graph that this container has been added to. >- */ >- public Graph getGraph() { >- return this.graph.getGraph(); >- } >- >- public int getItemType() { >- return CONTAINER; >- } >- >- /** >- * >- */ >- public void setLayoutAlgorithm(LayoutAlgorithm algorithm, boolean applyLayout) { >- this.layoutAlgorithm = algorithm; >- if (applyLayout) { >- applyLayout(); >- } >- >- } >- >- public void applyLayout() { >- if ((this.getNodes().size() == 0)) { >- return; >- } >- >- int layoutStyle = 0; >- >- if (checkStyle(ZestStyles.NODES_NO_LAYOUT_RESIZE)) { >- layoutStyle = LayoutStyles.NO_LAYOUT_NODE_RESIZING; >- } >- >- if (layoutAlgorithm == null) { >- layoutAlgorithm = new TreeLayoutAlgorithm(layoutStyle); >- } >- >- layoutAlgorithm.setStyle(layoutAlgorithm.getStyle() | layoutStyle); >- >- // calculate the size for the layout algorithm >- //Dimension d = this.scalledLayer.getSize(); >- Dimension d = new Dimension(); >- d.width = (int) scaledWidth; >- d.height = (int) scaledHeight; >- >- d.width = d.width - 10; >- d.height = d.height - 10; >- //if (d.height <= 0) { >- //d.height = (CONTAINER_HEIGHT); >- //} >- //d.scale(1 / this.scalledLayer.getScale()); >- >- if (d.isEmpty()) { >- return; >- } >- LayoutRelationship[] connectionsToLayout = getGraph().getConnectionsToLayout(getNodes()); >- LayoutEntity[] nodesToLayout = getGraph().getNodesToLayout(getNodes()); >- >- try { >- Animation.markBegin(); >- layoutAlgorithm.applyLayout(nodesToLayout, connectionsToLayout, 25, 25, d.width - 50, d.height - 50, false, false); >- Animation.run(ANIMATION_TIME); >- getFigure().getUpdateManager().performUpdate(); >- >- } catch (InvalidLayoutConfiguration e) { >- e.printStackTrace(); >- } >- >- } >- >- /** >- * Get the scale for this container. This is the scale applied to the children contained within >- * @return >- */ >- public double getScale() { >- return this.scalledLayer.getScale(); >- } >- >- /** >- * Set the scale for this container. This is the scale applied to the children contained within. >- * @param scale >- */ >- public void setScale(double scale) { >- this.scalledLayer.setScale(scale); >- } >- >- /*************************************************************************** >- * NON API MEMBERS >- **************************************************************************/ >- protected void initFigure() { >- nodeFigure = createContainerFigure(); >- } >- >- /** >- * This is a small class to help represent the size of the container. It should only be >- * used in the computeContainerSize method. >- */ >- class ContainerDimension { >- int width; >- int labelHeight; >- int expandedHeight; >- } >- >- /** >- * Computes size of the scroll pane that the child nodes will be placed in. >- * @return >- */ >- private Dimension computeChildArea() { >- ContainerDimension containerDimension = computeContainerSize(); >- Dimension dimension = new Dimension(); >- dimension.width = containerDimension.width; >- dimension.height = containerDimension.expandedHeight - containerDimension.labelHeight + SUBLAYER_OFFSET; >- return dimension; >- } >- >- /** >- * Computes the desired size of the container. This method uses the >- * minimum size, label size and setSize to compute the size. >- * @return >- */ >- private ContainerDimension computeContainerSize() { >- ContainerDimension dimension = new ContainerDimension(); >- int labelHeight = expandGraphLabel.getPreferredSize().height; >- int labelWidth = expandGraphLabel.getPreferredSize().width; >- if (labelWidth < MIN_WIDTH) { >- labelWidth = MIN_WIDTH; >- expandGraphLabel.setPreferredSize(labelWidth, labelHeight); >- } >- if (labelHeight < 30) { >- labelHeight = 30; >- } >- >- dimension.labelHeight = labelHeight; >- dimension.width = labelWidth; >- dimension.width = Math.max(dimension.width, this.size.width); >- dimension.expandedHeight = dimension.labelHeight + childAreaHeight - SUBLAYER_OFFSET; >- dimension.expandedHeight = Math.max(dimension.expandedHeight, this.size.height); >- >- return dimension; >- } >- >- /* >- private double computeChildScale() { >- Dimension childArea = computeChildArea(); >- double widthScale = childArea.width / scaledWidth; >- double heightScale = childArea.height / scaledHeight; >- return Math.min(widthScale, heightScale); >- } >- */ >- private double computeHeightScale() { >- Dimension childArea = computeChildArea(); >- double heightScale = childArea.height / scaledHeight; >- return heightScale; >- } >- >- private double computeWidthScale() { >- Dimension childArea = computeChildArea(); >- double widthScale = childArea.width / scaledWidth; >- return widthScale; >- } >- >- private IFigure createContainerFigure() { >- GraphContainer node = this; >- IFigure containerFigure = new ContainerFigure(); >- containerFigure.setOpaque(true); >- >- containerFigure.addLayoutListener(LayoutAnimator.getDefault()); >- >- containerFigure.setLayoutManager(new FreeformLayout()); >- expandGraphLabel = new ExpandGraphLabel(this, node.getText(), node.getImage(), false); >- expandGraphLabel.setText(getText()); >- expandGraphLabel.setImage(getImage()); >- ContainerDimension containerDimension = computeContainerSize(); >- >- scrollPane = new ScrollPane(); >- scrollPane.addLayoutListener(LayoutAnimator.getDefault()); >- >- Viewport viewport = new FreeformViewport(); >- /* >- * This is the code that helps remove the scroll bars moving when the nodes >- * are dragged. >- * >- viewport.setHorizontalRangeModel(new DefaultRangeModel() { >- public void setAll(int min, int ext, int max) { >- System.out.println("Max: " + max + " : current Max: " + getMaximum()); >- if (max < getMaximum()) { >- max = getMaximum(); >- } >- super.setAll(min, ext, max); >- } >- >- public void setMaximum(int maximum) { >- // TODO Auto-generated method stub >- System.out.println("Max: " + maximum + " : current Max: " + getMaximum()); >- if (maximum < getMaximum()) { >- return; >- } >- super.setMaximum(maximum); >- } >- }); >- */ >- >- scrollPane.setViewport(viewport); >- viewport.addLayoutListener(LayoutAnimator.getDefault()); >- scrollPane.setScrollBarVisibility(ScrollPane.AUTOMATIC); >- >- //scalledLayer = new ScalableFreeformLayeredPane(); >- scalledLayer = new AspectRatioFreeformLayer("debug label"); >- scalledLayer.addLayoutListener(LayoutAnimator.getDefault()); >- //scalledLayer.setScale(computeChildScale()); >- scalledLayer.setScale(computeWidthScale(), computeHeightScale()); >- //container = new FreeformLayer(); >- //edgeLayer = new FreeformLayer(); >- zestLayer = new ZestRootLayer(); >- zestLayer.addLayoutListener(LayoutAnimator.getDefault()); >- //container.addLayoutListener(LayoutAnimator.getDefault()); >- //edgeLayer.addLayoutListener(LayoutAnimator.getDefault()); >- //scalledLayer.add(edgeLayer); >- //scalledLayer.add(container); >- scalledLayer.add(zestLayer); >- >- //container.setLayoutManager(new FreeformLayout()); >- zestLayer.setLayoutManager(new FreeformLayout()); >- scrollPane.setSize(computeChildArea()); >- scrollPane.setLocation(new Point(0, containerDimension.labelHeight - SUBLAYER_OFFSET)); >- scrollPane.setForegroundColor(ColorConstants.gray); >- >- expandGraphLabel.setBackgroundColor(getBackgroundColor()); >- expandGraphLabel.setForegroundColor(getForegroundColor()); >- expandGraphLabel.setLocation(new Point(0, 0)); >- >- containerFigure.add(scrollPane); >- containerFigure.add(expandGraphLabel); >- >- scrollPane.getViewport().setContents(scalledLayer); >- scrollPane.setBorder(new LineBorder()); >- >- return containerFigure; >- } >- >- protected void updateFigureForModel(IFigure currentFigure) { >- >- expandGraphLabel.setTextT(getText()); >- expandGraphLabel.setImage(getImage()); >- expandGraphLabel.setFont(getFont()); >- >- if (highlighted == HIGHLIGHT_ON) { >- expandGraphLabel.setForegroundColor(getForegroundColor()); >- expandGraphLabel.setBackgroundColor(getHighlightColor()); >- } >- // @tag ADJACENT : Removed highlight adjacent >- /* >- else if (highlighted == HIGHLIGHT_ADJACENT) { >- expandGraphLabel.setForegroundColor(getForegroundColor()); >- expandGraphLabel.setBackgroundColor(getHighlightAdjacentColor()); >- } >- */ >- else { >- expandGraphLabel.setForegroundColor(getForegroundColor()); >- expandGraphLabel.setBackgroundColor(getBackgroundColor()); >- } >- >- ContainerDimension containerDimension = computeContainerSize(); >- >- expandGraphLabel.setSize(containerDimension.width, containerDimension.labelHeight); >- if (isExpanded) { >- //setSize(expandGraphLabel.getSize().width, expandGraphLabel.getSize().height + expandedHeight - SUBLAYER_OFFSET); >- setSize(containerDimension.width, containerDimension.expandedHeight); >- } else { >- setSize(containerDimension.width, containerDimension.labelHeight); >- } >- scrollPane.setLocation(new Point(expandGraphLabel.getLocation().x, expandGraphLabel.getLocation().y + containerDimension.labelHeight - SUBLAYER_OFFSET)); >- //scrollPane.setLocation(new Point(0, labelHeight - SUBLAYER_OFFSET)); >- //Rectangle bounds = expandGraphLabel.getBounds().getCopy(); >- //Rectangle newBounds = new Rectangle(new Point(bounds.x, bounds.y + labelHeight - SUBLAYER_OFFSET), scrollPane.getSize()); >- //figure.setConstraint(scrollPane, newBounds); >- /* >- size.width = labelWidth; >- if (scrollPane.getSize().height > 0) { >- size.height = labelHeight + scrollPane.getSize().height - SUBLAYER_OFFSET; >- } else { >- size.height = labelHeight; >- } >- refreshLocation(); >- figure.getUpdateManager().performValidation(); >- */ >- >- } >- >- protected void refreshLocation() { >- if (nodeFigure == null || nodeFigure.getParent() == null) { >- return; // node figure has not been created yet >- } >- GraphNode node = this; >- Point loc = node.getLocation(); >- >- ContainerDimension containerDimension = computeContainerSize(); >- Dimension size = new Dimension(); >- >- expandGraphLabel.setSize(containerDimension.width, containerDimension.labelHeight); >- this.childAreaHeight = computeChildArea().height; >- if (isExpanded) { >- size.width = containerDimension.width; >- size.height = containerDimension.expandedHeight; >- } else { >- size.width = containerDimension.width; >- size.height = containerDimension.labelHeight; >- } >- Rectangle bounds = new Rectangle(loc, size); >- nodeFigure.getParent().setConstraint(nodeFigure, bounds); >- scrollPane.setLocation(new Point(expandGraphLabel.getLocation().x, expandGraphLabel.getLocation().y + containerDimension.labelHeight - SUBLAYER_OFFSET)); >- scrollPane.setSize(computeChildArea()); >- scalledLayer.setScale(computeWidthScale(), computeHeightScale()); >- } >- >- void addConnectionFigure(PolylineConnection connection) { >- nodeFigure.add(connection); >- //zestLayer.addConnection(connection); >- } >- >- void addNode(GraphNode node) { >- zestLayer.addNode(node.getNodeFigure()); >- this.childNodes.add(node); >- //container.add(node.getNodeFigure()); >- //graph.registerItem(node); >- } >- >- void addNode(GraphContainer container) { >- // Containers cannot be added to other containers (yet) >- } >- >- public List getNodes() { >- return this.childNodes; >- } >- >- void paint() { >- Iterator iterator = getNodes().iterator(); >- >- while (iterator.hasNext()) { >- GraphNode node = (GraphNode) iterator.next(); >- node.paint(); >- } >- } >- >-} >+/******************************************************************************* >+ * Copyright 2005-2010, 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 >+ * Mateusz Matela >+ ******************************************************************************/ >+package org.eclipse.zest.core.widgets; >+ >+import java.util.ArrayList; >+import java.util.Iterator; >+import java.util.LinkedList; >+import java.util.List; >+ >+import org.eclipse.draw2d.ActionEvent; >+import org.eclipse.draw2d.ActionListener; >+import org.eclipse.draw2d.Animation; >+import org.eclipse.draw2d.Clickable; >+import org.eclipse.draw2d.ColorConstants; >+import org.eclipse.draw2d.Figure; >+import org.eclipse.draw2d.FreeformLayout; >+import org.eclipse.draw2d.FreeformViewport; >+import org.eclipse.draw2d.Graphics; >+import org.eclipse.draw2d.IFigure; >+import org.eclipse.draw2d.Label; >+import org.eclipse.draw2d.LayoutAnimator; >+import org.eclipse.draw2d.LineBorder; >+import org.eclipse.draw2d.ScrollPane; >+import org.eclipse.draw2d.ToolbarLayout; >+import org.eclipse.draw2d.Triangle; >+import org.eclipse.draw2d.Viewport; >+import org.eclipse.draw2d.geometry.Dimension; >+import org.eclipse.draw2d.geometry.Point; >+import org.eclipse.draw2d.geometry.Rectangle; >+import org.eclipse.swt.events.SelectionEvent; >+import org.eclipse.swt.events.SelectionListener; >+import org.eclipse.swt.graphics.Color; >+import org.eclipse.swt.graphics.Image; >+import org.eclipse.swt.graphics.RGB; >+import org.eclipse.swt.widgets.Display; >+import org.eclipse.swt.widgets.Widget; >+import org.eclipse.zest.core.widgets.internal.AspectRatioFreeformLayer; >+import org.eclipse.zest.core.widgets.internal.ContainerFigure; >+import org.eclipse.zest.core.widgets.internal.ZestRootLayer; >+import org.eclipse.zest.layouts.LayoutAlgorithm; >+import org.eclipse.zest.layouts.algorithms.TreeLayoutAlgorithm; >+import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle; >+ >+/** >+ * A Container that can be added to a Graph. Nodes can be added to this >+ * container. The container supports collapsing and expanding and has the same >+ * properties as the nodes. Containers cannot have custom figures. >+ * >+ * @author Ian Bull >+ */ >+public class GraphContainer extends GraphNode implements IContainer { >+ >+ static class ExpandGraphLabel extends Figure implements ActionListener { >+ >+ private boolean isExpanded; >+ private Expander expander = new Expander(); >+ private Color darkerBackground; >+ >+ class Expander extends Clickable { >+ private Triangle triangle; >+ >+ public Expander() { >+ setStyle(Clickable.STYLE_TOGGLE); >+ triangle = new Triangle(); >+ triangle.setSize(10, 10); >+ triangle.setBackgroundColor(ColorConstants.black); >+ triangle.setForegroundColor(ColorConstants.black); >+ triangle.setFill(true); >+ triangle.setDirection(Triangle.EAST); >+ triangle.setLocation(new Point(5, 3)); >+ this.setLayoutManager(new FreeformLayout()); >+ this.add(triangle); >+ this.setPreferredSize(15, 15); >+ this.addActionListener(ExpandGraphLabel.this); >+ } >+ >+ public void open() { >+ triangle.setDirection(Triangle.SOUTH); >+ } >+ >+ public void close() { >+ triangle.setDirection(Triangle.EAST); >+ } >+ >+ } >+ >+ /** >+ * Sets the expander state (the little triangle) to >+ * ExpanderGraphLabel.OPEN or ExpanderGraphLabel.CLOSED >+ * >+ * @param state >+ */ >+ public void setExpandedState(boolean expanded) { >+ if (expanded) { >+ expander.open(); >+ } else { >+ expander.close(); >+ } >+ this.isExpanded = expanded; >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see >+ * org.eclipse.draw2d.ActionListener#actionPerformed(org.eclipse.draw2d >+ * .ActionEvent) >+ */ >+ public void actionPerformed(ActionEvent event) { >+ if (isExpanded) { >+ container.close(true); >+ } else { >+ container.open(true); >+ } >+ } >+ >+ private final int arcWidth = 8; >+ private final Label label; >+ private final GraphContainer container; >+ private final ToolbarLayout layout; >+ >+ public ExpandGraphLabel(GraphContainer container, String text, >+ Image image, boolean cacheLabel) { >+ this.label = new Label(text) { >+ >+ /** >+ * <b>This method is overwritten so that the text is not >+ * truncated.</b><br> >+ * >+ * {@inheritDoc} >+ * >+ */ >+ protected void paintFigure(Graphics graphics) { >+ if (isOpaque()) { >+ super.paintFigure(graphics); >+ } >+ Rectangle bounds = getBounds(); >+ graphics.translate(bounds.x, bounds.y); >+ if (getIcon() != null) { >+ graphics.drawImage(getIcon(), getIconLocation()); >+ } >+ if (!isEnabled()) { >+ graphics.translate(1, 1); >+ graphics.setForegroundColor(ColorConstants.buttonLightest); >+ graphics.drawText(getSubStringText(), getTextLocation()); >+ graphics.translate(-1, -1); >+ graphics.setForegroundColor(ColorConstants.buttonDarker); >+ } >+ graphics.drawText(getText(), getTextLocation()); >+ graphics.translate(-bounds.x, -bounds.y); >+ } >+ }; >+ this.setText(text); >+ this.setImage(image); >+ this.container = container; >+ this.setFont(Display.getDefault().getSystemFont()); >+ layout = new ToolbarLayout(true); >+ layout.setSpacing(5); >+ layout.setMinorAlignment(ToolbarLayout.ALIGN_CENTER); >+ this.setLayoutManager(layout); >+ this.add(this.expander); >+ this.add(this.label); >+ } >+ >+ private Color getDarkerBackgroundColor() { >+ if (darkerBackground == null) { >+ Color baseColor = getBackgroundColor(); >+ int blue = (int) (baseColor.getBlue() * 0.8 + 0.5); >+ int red = (int) (baseColor.getRed() * 0.8 + 0.5); >+ int green = (int) (baseColor.getGreen() * 0.8 + 0.5); >+ darkerBackground = new Color(Display.getCurrent(), new RGB(red, >+ green, blue)); >+ } >+ return darkerBackground; >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see >+ * org.eclipse.draw2d.Label#paintFigure(org.eclipse.draw2d.Graphics) >+ */ >+ public void paint(Graphics graphics) { >+ >+ graphics.setForegroundColor(getDarkerBackgroundColor()); >+ graphics.setBackgroundColor(getBackgroundColor()); >+ >+ graphics.pushState(); >+ >+ // fill in the background >+ Rectangle bounds = getBounds().getCopy(); >+ Rectangle r = bounds.getCopy(); >+ r.y += arcWidth / 2; >+ r.height -= arcWidth; >+ >+ Rectangle top = bounds.getCopy(); >+ top.height /= 2; >+ graphics.setForegroundColor(getBackgroundColor()); >+ graphics.setBackgroundColor(getBackgroundColor()); >+ graphics.fillRoundRectangle(top, arcWidth, arcWidth); >+ >+ top.y = top.y + top.height; >+ graphics.setForegroundColor(darkerBackground); >+ graphics.setBackgroundColor(darkerBackground); >+ graphics.fillRoundRectangle(top, arcWidth, arcWidth); >+ >+ graphics.setBackgroundColor(darkerBackground); >+ graphics.setForegroundColor(getBackgroundColor()); >+ graphics.fillGradient(r, true); >+ >+ super.paint(graphics); >+ graphics.popState(); >+ graphics.setForegroundColor(darkerBackground); >+ graphics.setBackgroundColor(darkerBackground); >+ // paint the border >+ bounds.setSize(bounds.width - 1, bounds.height - 1); >+ graphics.drawRoundRectangle(bounds, arcWidth, arcWidth); >+ } >+ >+ public void setBackgroundColor(Color bg) { >+ super.setBackgroundColor(bg); >+ if (darkerBackground != null) { >+ darkerBackground.dispose(); >+ } >+ darkerBackground = null; >+ } >+ >+ public void setTextT(String string) { >+ this.setPreferredSize(null); >+ this.label.setText(string); >+ this.add(label); >+ this.layout.layout(this); >+ this.invalidate(); >+ this.revalidate(); >+ this.validate(); >+ } >+ >+ public void setText(String string) { >+ this.label.setText(string); >+ } >+ >+ public void setImage(Image image) { >+ this.label.setIcon(image); >+ } >+ >+ public void setFocus() { >+ expander.requestFocus(); >+ } >+ >+ } >+ >+ static final double SCALED_WIDTH = 300; >+ static final double SCALED_HEIGHT = 200; >+ private static final int CONTAINER_HEIGHT = 200; >+ private static final int MIN_WIDTH = 250; >+ private static final int MIN_HEIGHT = 30; >+ private static final int ANIMATION_TIME = 100; >+ private static final int SUBLAYER_OFFSET = 2; >+ >+ private static SelectionListener selectionListener; >+ >+ private ExpandGraphLabel expandGraphLabel; >+ >+ private List childNodes = null; >+ private int childAreaHeight = CONTAINER_HEIGHT; >+ >+ private ZestRootLayer zestLayer; >+ private ScrollPane scrollPane; >+ private LayoutAlgorithm layoutAlgorithm; >+ private boolean isExpanded = false; >+ private AspectRatioFreeformLayer scalledLayer; >+ private InternalLayoutContext layoutContext; >+ >+ /** >+ * Creates a new GraphContainer. A GraphContainer may contain nodes, and has >+ * many of the same properties as a graph node. >+ * >+ * @param graph >+ * The graph that the container is being added to >+ * @param style >+ * >+ * @since 2.0 >+ */ >+ public GraphContainer(Graph graph, int style) { >+ super(graph, style, ""); >+ initModel(graph, "", null); >+ close(false); >+ childNodes = new ArrayList(); >+ registerToParent(graph); >+ } >+ >+ /** >+ * Custom figures cannot be set on a GraphContainer. >+ */ >+ public void setCustomFigure(IFigure nodeFigure) { >+ throw new RuntimeException( >+ "Operation not supported: Containers cannot have custom figures"); >+ } >+ >+ /** >+ * Close this node. >+ * >+ * @param animate >+ */ >+ public void close(boolean animate) { >+ if (animate) { >+ Animation.markBegin(); >+ } >+ isExpanded = false; >+ >+ expandGraphLabel.setExpandedState(false); >+ Rectangle newBounds = scrollPane.getBounds().getCopy(); >+ newBounds.height = 0; >+ >+ scrollPane.setSize(scrollPane.getSize().width, 0); >+ updateFigureForModel(this.zestLayer); >+ scrollPane.setVisible(false); >+ List children = this.zestLayer.getChildren(); >+ for (Iterator iterator = children.iterator(); iterator.hasNext();) { >+ IFigure child = (IFigure) iterator.next(); >+ GraphItem item = getGraph().getGraphItem(child); >+ item.setVisible(false); >+ } >+ Rectangle containerBounds = new Rectangle(this.getLocation(), >+ new Dimension(this.getSize().width, CONTAINER_HEIGHT >+ + this.expandGraphLabel.getSize().height)); >+ moveNodesUp(containerBounds, this); >+ if (animate) { >+ Animation.run(ANIMATION_TIME); >+ } >+ updateFigureForModel(nodeFigure); >+ } >+ >+ private static void addNodeToOrderedList(List orderedNodeList, >+ GraphNode node) { >+ Iterator orderedNodeIterator = orderedNodeList.iterator(); >+ int counter = 0; >+ while (orderedNodeIterator.hasNext()) { >+ // Look through the list of nodes below and find the right spot for >+ // this >+ GraphNode nextOrderedNode = (GraphNode) orderedNodeIterator.next(); >+ if (nextOrderedNode.getLocation().y >+ + nextOrderedNode.getBounds().height > node.getLocation().y >+ + node.getBounds().height) { >+ break; >+ } >+ counter++; >+ } >+ // Place this in the right location >+ orderedNodeList.add(counter, node); >+ } >+ >+ /** >+ * Gets all the nodes below the yValue. The nodes are returned in order. >+ * >+ * @param nodes >+ * @param yValue >+ * @return >+ */ >+ private static List getOrderedNodesBelowY(List nodes, int yValue, >+ GraphNode yValueNode) { >+ Iterator iterator = nodes.iterator(); >+ LinkedList orderedNode = new LinkedList(); >+ while (iterator.hasNext()) { >+ GraphNode node = (GraphNode) iterator.next(); >+ if (node == yValueNode) { >+ continue; >+ } >+ if (node.getLocation().y + node.getBounds().height > yValue) { >+ // This node is below the container >+ addNodeToOrderedList(orderedNode, node); >+ } >+ } >+ // Convert this to an arrayList for faster access >+ List arrayList = new ArrayList(); >+ iterator = orderedNode.iterator(); >+ while (iterator.hasNext()) { >+ arrayList.add(iterator.next()); >+ } >+ return arrayList; >+ } >+ >+ /** >+ * Checks if the node intersects the stripe between left and right >+ * >+ * @param left >+ * @param right >+ * @param node >+ * @return >+ */ >+ private static boolean nodeInStripe(int left, int right, GraphNode node) { >+ return (node.getBounds().x < right && node.getBounds().x >+ + node.getBounds().width > left); >+ } >+ >+ void pack(Graph g) { >+ GraphNode highestNode = getHighestNode(g); >+ moveNodesUp(highestNode.getBounds(), highestNode); >+ } >+ >+ /** >+ * >+ * @param g >+ * @return >+ */ >+ static GraphNode getHighestNode(Graph g) { >+ Iterator iterator = g.getNodes().iterator(); >+ GraphNode lowest /* highest on the screen */= null; >+ >+ while (iterator.hasNext()) { >+ GraphNode node = (GraphNode) iterator.next(); >+ if (lowest == null || lowest.getBounds().y > node.getBounds().y) { >+ lowest = node; >+ } >+ } >+ return lowest; >+ >+ } >+ >+ /** >+ * Move the nodes below this node up >+ * >+ * @param containerBounds >+ * @param graphContainer >+ */ >+ private void moveNodesUp(Rectangle containerBounds, GraphNode graphContainer) { >+ >+ // Get all nodes below this container, in order >+ List orderedNodesBelowY = getOrderedNodesBelowY(parent.getGraph() >+ .getNodes(), containerBounds.y, graphContainer); >+ int leftSide = containerBounds.x; >+ int rightSide = containerBounds.x + containerBounds.width; >+ List nodesToConsider = new LinkedList(); >+ for (int i = 0; i < orderedNodesBelowY.size(); i++) { >+ nodesToConsider.add(orderedNodesBelowY.get(i)); >+ } >+ addNodeToOrderedList(orderedNodesBelowY, graphContainer); >+ >+ while (nodesToConsider.size() > 0) { >+ GraphNode node = (GraphNode) nodesToConsider.get(0); >+ if (nodeInStripe(leftSide, rightSide, node)) { >+ leftSide = Math.min(leftSide, node.getBounds().x); >+ rightSide = Math.max(rightSide, node.getBounds().x >+ + node.getBounds().width); >+ // If this node is in the stripe, move it up >+ // the previous node >+ GraphNode previousNode = null; >+ int i = 0; >+ for (; i < orderedNodesBelowY.size(); i++) { >+ if (orderedNodesBelowY.get(i) == node) { >+ break; >+ } >+ } >+ int j = i - 1; >+ while (j >= 0) { >+ GraphNode pastNode = (GraphNode) orderedNodesBelowY.get(j); >+ // if (nodeInStripe(leftSide, rightSide, pastNode)) { >+ if (nodeInStripe(node.getBounds().x, node.getBounds().x >+ + node.getBounds().width, pastNode)) { >+ previousNode = pastNode; >+ break; >+ } >+ j--; >+ } >+ if (previousNode == null) { >+ previousNode = graphContainer; >+ } >+ int previousLocation = previousNode.getBounds().y >+ + previousNode.getBounds().height + 2; >+ >+ orderedNodesBelowY.remove(i); >+ node.setLocation(node.getLocation().x, previousLocation); >+ addNodeToOrderedList(orderedNodesBelowY, node); >+ >+ } >+ nodesToConsider.remove(node); >+ } >+ } >+ >+ /** >+ * Open the container. This opens the graph container to show the nodes >+ * within and update the twistie >+ */ >+ public void open(boolean animate) { >+ if (animate) { >+ Animation.markBegin(); >+ } >+ isExpanded = true; >+ >+ expandGraphLabel.setExpandedState(true); >+ >+ scrollPane.setSize(computeChildArea()); >+ scrollPane.setVisible(true); >+ >+ List children = this.zestLayer.getChildren(); >+ for (Iterator iterator = children.iterator(); iterator.hasNext();) { >+ IFigure child = (IFigure) iterator.next(); >+ GraphItem item = getGraph().getGraphItem(child); >+ item.setVisible(true); >+ } >+ >+ updateFigureForModel(nodeFigure); >+ >+ Rectangle containerBounds = new Rectangle(this.getLocation(), >+ new Dimension(this.getSize().width, CONTAINER_HEIGHT >+ + this.expandGraphLabel.getSize().height)); >+ moveNodesDown(containerBounds, this); >+ moveNodesUp(containerBounds, this); >+ if (animate) { >+ Animation.run(ANIMATION_TIME); >+ } >+ this.getFigure().getUpdateManager().performValidation(); >+ } >+ >+ /** >+ * >+ * @param containerBounds >+ * @param graphContainer >+ */ >+ private void moveNodesDown(Rectangle containerBounds, >+ GraphContainer graphContainer) { >+ >+ // Find all nodes below here >+ List nodesBelowHere = getOrderedNodesBelowY(parent.getGraph() >+ .getNodes(), containerBounds.y, graphContainer); >+ Iterator nodesBelowHereIterator = nodesBelowHere.iterator(); >+ List nodesToMove = new LinkedList(); >+ int left = containerBounds.x; >+ int right = containerBounds.x + containerBounds.width; >+ while (nodesBelowHereIterator.hasNext()) { >+ GraphNode node = (GraphNode) nodesBelowHereIterator.next(); >+ if (nodeInStripe(left, right, node)) { >+ nodesToMove.add(node); >+ left = Math.min(left, node.getBounds().x); >+ right = Math.max(right, node.getBounds().x >+ + node.getBounds().width); >+ } >+ } >+ List intersectingNodes = intersectingNodes(containerBounds, >+ nodesToMove, graphContainer); >+ int delta = getMaxMovement(containerBounds, intersectingNodes); >+ if (delta > 0) { >+ shiftNodesDown(nodesToMove, delta); >+ } >+ >+ } >+ >+ /** >+ * Checks all the nodes in the list of nodesToCheck to see if they intersect >+ * with the bounds set >+ * >+ * @param node >+ * @param nodesToCheck >+ * @return >+ */ >+ private List intersectingNodes(Rectangle bounds, List nodesToCheck, >+ GraphNode node) { >+ List result = new LinkedList(); >+ Iterator nodes = nodesToCheck.iterator(); >+ while (nodes.hasNext()) { >+ GraphNode nodeToCheck = (GraphNode) nodes.next(); >+ if (node == nodeToCheck) { >+ continue; >+ } >+ if (bounds.intersects(nodeToCheck.getBounds())) { >+ result.add(nodeToCheck); >+ } >+ } >+ return result; >+ } >+ >+ /** >+ * Gets the max distance the intersecting nodes need to be shifted to make >+ * room for the expanding node >+ * >+ * @param bounds >+ * @param nodesToMove >+ * @return >+ */ >+ private int getMaxMovement(Rectangle bounds, List nodesToMove) { >+ Iterator iterator = nodesToMove.iterator(); >+ int maxMovement = 0; >+ while (iterator.hasNext()) { >+ GraphNode node = (GraphNode) iterator.next(); >+ int yValue = node.getLocation().y; >+ int distanceFromBottom = (bounds.y + bounds.height) - yValue; >+ maxMovement = Math.max(maxMovement, distanceFromBottom); >+ } >+ return maxMovement + 3; >+ } >+ >+ /** >+ * Shifts a collection of nodes down. >+ * >+ * @param nodesToShift >+ * @param amount >+ */ >+ private void shiftNodesDown(List nodesToShift, int amount) { >+ Iterator iterator = nodesToShift.iterator(); >+ while (iterator.hasNext()) { >+ GraphNode node = (GraphNode) iterator.next(); >+ >+ node.setLocation(node.getLocation().x, node.getLocation().y >+ + amount); >+ } >+ } >+ >+ /** >+ * Gets the graph that this container has been added to. >+ */ >+ public Graph getGraph() { >+ return this.graph; >+ } >+ >+ /** >+ * @since 2.0 >+ */ >+ public Widget getItem() { >+ return this; >+ } >+ >+ public int getItemType() { >+ return CONTAINER; >+ } >+ >+ /** >+ * @since 2.0 >+ */ >+ public void setLayoutAlgorithm(LayoutAlgorithm algorithm, >+ boolean applyLayout) { >+ if (this.layoutAlgorithm != null) { >+ this.layoutAlgorithm.setLayoutContext(null); >+ } >+ >+ this.layoutAlgorithm = algorithm; >+ this.layoutAlgorithm.setLayoutContext(getLayoutContext()); >+ if (applyLayout) { >+ applyLayout(); >+ } >+ } >+ >+ /** >+ * @noreference This method is not intended to be referenced by clients. >+ */ >+ public InternalLayoutContext getLayoutContext() { >+ if (layoutContext == null) { >+ layoutContext = new InternalLayoutContext(this); >+ } >+ return layoutContext; >+ } >+ >+ /** >+ * @since 2.0 >+ */ >+ public DisplayIndependentRectangle getLayoutBounds() { >+ double width = GraphContainer.SCALED_WIDTH - 10; >+ double height = GraphContainer.SCALED_HEIGHT - 10; >+ return new DisplayIndependentRectangle(25, 25, width - 50, height - 50); >+ } >+ >+ public void applyLayout() { >+ if (layoutAlgorithm == null) { >+ setLayoutAlgorithm(new TreeLayoutAlgorithm(), false); >+ } >+ Animation.markBegin(); >+ layoutAlgorithm.applyLayout(true); >+ layoutContext.flushChanges(false); >+ Animation.run(ANIMATION_TIME); >+ getFigure().getUpdateManager().performUpdate(); >+ } >+ >+ /** >+ * Get the scale for this container. This is the scale applied to the >+ * children contained within >+ * >+ * @return >+ */ >+ public double getScale() { >+ return this.scalledLayer.getScale(); >+ } >+ >+ /** >+ * Set the scale for this container. This is the scale applied to the >+ * children contained within. >+ * >+ * @param scale >+ */ >+ public void setScale(double scale) { >+ this.scalledLayer.setScale(scale); >+ } >+ >+ /*************************************************************************** >+ * NON API MEMBERS >+ **************************************************************************/ >+ protected void initFigure() { >+ nodeFigure = createContainerFigure(); >+ } >+ >+ /** >+ * This is a small class to help represent the size of the container. It >+ * should only be used in the computeContainerSize method. >+ */ >+ class ContainerDimension { >+ int width; >+ int labelHeight; >+ int expandedHeight; >+ } >+ >+ /** >+ * Computes size of the scroll pane that the child nodes will be placed in. >+ * >+ * @return >+ */ >+ private Dimension computeChildArea() { >+ ContainerDimension containerDimension = computeContainerSize(); >+ Dimension dimension = new Dimension(); >+ dimension.width = containerDimension.width; >+ dimension.height = containerDimension.expandedHeight >+ - containerDimension.labelHeight + SUBLAYER_OFFSET; >+ return dimension; >+ } >+ >+ /** >+ * Computes the desired size of the container. This method uses the minimum >+ * size, label size and setSize to compute the size. >+ * >+ * @return >+ */ >+ private ContainerDimension computeContainerSize() { >+ ContainerDimension dimension = new ContainerDimension(); >+ int labelHeight = expandGraphLabel.getPreferredSize().height; >+ int labelWidth = expandGraphLabel.getPreferredSize().width; >+ if (labelWidth < MIN_WIDTH) { >+ labelWidth = MIN_WIDTH; >+ expandGraphLabel.setPreferredSize(labelWidth, labelHeight); >+ } >+ >+ dimension.labelHeight = Math.max(labelHeight, MIN_HEIGHT); >+ dimension.width = Math.max(labelWidth, this.size.width); >+ dimension.expandedHeight = Math.max(dimension.labelHeight >+ + childAreaHeight - SUBLAYER_OFFSET, this.size.height); >+ >+ return dimension; >+ } >+ >+ private double computeHeightScale() { >+ Dimension childArea = computeChildArea(); >+ double heightScale = childArea.height / SCALED_HEIGHT; >+ return heightScale; >+ } >+ >+ private double computeWidthScale() { >+ Dimension childArea = computeChildArea(); >+ double widthScale = childArea.width / SCALED_WIDTH; >+ return widthScale; >+ } >+ >+ private IFigure createContainerFigure() { >+ GraphContainer node = this; >+ IFigure containerFigure = new ContainerFigure(); >+ containerFigure.setOpaque(true); >+ >+ containerFigure.addLayoutListener(LayoutAnimator.getDefault()); >+ >+ containerFigure.setLayoutManager(new FreeformLayout()); >+ expandGraphLabel = new ExpandGraphLabel(this, node.getText(), node >+ .getImage(), false); >+ expandGraphLabel.setText(getText()); >+ expandGraphLabel.setImage(getImage()); >+ ContainerDimension containerDimension = computeContainerSize(); >+ >+ scrollPane = new ScrollPane(); >+ scrollPane.addLayoutListener(LayoutAnimator.getDefault()); >+ >+ Viewport viewport = new FreeformViewport(); >+ >+ scrollPane.setViewport(viewport); >+ viewport.addLayoutListener(LayoutAnimator.getDefault()); >+ scrollPane.setScrollBarVisibility(ScrollPane.AUTOMATIC); >+ >+ scalledLayer = new AspectRatioFreeformLayer("debug label"); >+ scalledLayer.addLayoutListener(LayoutAnimator.getDefault()); >+ scalledLayer.setScale(computeWidthScale(), computeHeightScale()); >+ zestLayer = new ZestRootLayer(); >+ zestLayer.addLayoutListener(LayoutAnimator.getDefault()); >+ scalledLayer.add(zestLayer); >+ >+ zestLayer.setLayoutManager(new FreeformLayout()); >+ scrollPane.setSize(computeChildArea()); >+ scrollPane.setLocation(new Point(0, containerDimension.labelHeight >+ - SUBLAYER_OFFSET)); >+ scrollPane.setForegroundColor(ColorConstants.gray); >+ >+ expandGraphLabel.setBackgroundColor(getBackgroundColor()); >+ expandGraphLabel.setForegroundColor(getForegroundColor()); >+ expandGraphLabel.setLocation(new Point(0, 0)); >+ >+ containerFigure.add(scrollPane); >+ containerFigure.add(expandGraphLabel); >+ >+ scrollPane.getViewport().setContents(scalledLayer); >+ scrollPane.setBorder(new LineBorder()); >+ >+ return containerFigure; >+ } >+ >+ private void registerToParent(IContainer parent) { >+ if (parent.getItemType() == GRAPH) { >+ createSelectionListener(); >+ parent.getGraph().addSelectionListener(selectionListener); >+ } >+ } >+ >+ private void createSelectionListener() { >+ if (selectionListener == null) { >+ selectionListener = new SelectionListener() { >+ public void widgetSelected(SelectionEvent e) { >+ if (e.item instanceof GraphContainer) { >+ // set focus to expand label so that pressing space >+ // opens/closes >+ // the last selected container >+ ((GraphContainer) e.item).expandGraphLabel.setFocus(); >+ } >+ } >+ >+ public void widgetDefaultSelected(SelectionEvent e) { >+ // ignore >+ } >+ }; >+ >+ } >+ } >+ >+ protected void updateFigureForModel(IFigure currentFigure) { >+ >+ if (expandGraphLabel == null) { >+ initFigure(); >+ } >+ expandGraphLabel.setTextT(getText()); >+ expandGraphLabel.setImage(getImage()); >+ expandGraphLabel.setFont(getFont()); >+ >+ if (highlighted == HIGHLIGHT_ON) { >+ expandGraphLabel.setForegroundColor(getForegroundColor()); >+ expandGraphLabel.setBackgroundColor(getHighlightColor()); >+ } else { >+ expandGraphLabel.setForegroundColor(getForegroundColor()); >+ expandGraphLabel.setBackgroundColor(getBackgroundColor()); >+ } >+ >+ ContainerDimension containerDimension = computeContainerSize(); >+ >+ expandGraphLabel.setSize(containerDimension.width, >+ containerDimension.labelHeight); >+ if (isExpanded) { >+ setSize(containerDimension.width, containerDimension.expandedHeight); >+ } else { >+ setSize(containerDimension.width, containerDimension.labelHeight); >+ } >+ scrollPane.setLocation(new Point(expandGraphLabel.getLocation().x, >+ expandGraphLabel.getLocation().y >+ + containerDimension.labelHeight - SUBLAYER_OFFSET)); >+ >+ } >+ >+ void refreshBounds() { >+ if (nodeFigure == null || nodeFigure.getParent() == null) { >+ return; // node figure has not been created yet >+ } >+ GraphNode node = this; >+ Point loc = node.getLocation(); >+ >+ ContainerDimension containerDimension = computeContainerSize(); >+ Dimension size = new Dimension(); >+ >+ expandGraphLabel.setSize(containerDimension.width, >+ containerDimension.labelHeight); >+ this.childAreaHeight = computeChildArea().height; >+ if (isExpanded) { >+ size.width = containerDimension.width; >+ size.height = containerDimension.expandedHeight; >+ } else { >+ size.width = containerDimension.width; >+ size.height = containerDimension.labelHeight; >+ } >+ Rectangle bounds = new Rectangle(loc, size); >+ nodeFigure.getParent().setConstraint(nodeFigure, bounds); >+ scrollPane.setLocation(new Point(expandGraphLabel.getLocation().x, >+ expandGraphLabel.getLocation().y >+ + containerDimension.labelHeight - SUBLAYER_OFFSET)); >+ scrollPane.setSize(computeChildArea()); >+ scalledLayer.setScale(computeWidthScale(), computeHeightScale()); >+ } >+ >+ /** >+ * @noreference This method is not intended to be referenced by clients. >+ */ >+ public void addSubgraphFigure(IFigure figure) { >+ zestLayer.addSubgraph(figure); >+ graph.subgraphFigures.add(figure); >+ } >+ >+ void addConnectionFigure(IFigure figure) { >+ nodeFigure.add(figure); >+ } >+ >+ /** >+ * @noreference This method is not intended to be referenced by clients. >+ */ >+ public void addNode(GraphNode node) { >+ zestLayer.addNode(node.getNodeFigure()); >+ this.childNodes.add(node); >+ node.setVisible(isExpanded); >+ } >+ >+ public List getNodes() { >+ return this.childNodes; >+ } >+ >+ /** >+ * @since 2.0 >+ */ >+ public List getConnections() { >+ return filterConnections(getGraph().getConnections()); >+ >+ } >+ >+ private List filterConnections(List connections) { >+ List result = new ArrayList(); >+ for (Iterator iterator = connections.iterator(); iterator.hasNext();) { >+ GraphConnection connection = (GraphConnection) iterator.next(); >+ if (connection.getSource().getParent() == this >+ && connection.getDestination().getParent() == this) { >+ result.add(connection); >+ } >+ } >+ return result; >+ } >+} >Index: src/org/eclipse/zest/core/widgets/GraphItem.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/widgets/GraphItem.java,v >retrieving revision 1.9 >diff -u -r1.9 GraphItem.java >--- src/org/eclipse/zest/core/widgets/GraphItem.java 12 Sep 2007 20:44:39 -0000 1.9 >+++ src/org/eclipse/zest/core/widgets/GraphItem.java 15 Mar 2010 03:48:58 -0000 >@@ -1,114 +1,117 @@ >-/******************************************************************************* >- * Copyright 2005, 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.widgets; >- >-import org.eclipse.draw2d.IFigure; >-import org.eclipse.swt.SWT; >-import org.eclipse.swt.widgets.Item; >-import org.eclipse.swt.widgets.Widget; >- >-/** >- * Provides support for property changes. All model elements extend this class. >- * Also extends the Item (Widget) class to be used inside a StructuredViewer. >- * >- * @author Chris Callendar >- */ >-public abstract class GraphItem extends Item { >- >- public static final int GRAPH = 0; >- public static final int NODE = 1; >- public static final int CONNECTION = 2; >- public static final int CONTAINER = 3; >- >- /** >- * @param parent >- * @param style >- */ >- public GraphItem(Widget parent, int style) { >- this(parent, style | SWT.NO_BACKGROUND, null); >- } >- >- /** >- * @param parent >- * @param style >- */ >- public GraphItem(Widget parent, int style, Object data) { >- super(parent, style | SWT.NO_BACKGROUND); >- if (data != null) { >- this.setData(data); >- } >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.swt.widgets.Widget#dispose() >- */ >- public void dispose() { >- // @tag zest.bug.167132-ListenerDispose : remove all listeners. >- // pcsDelegate = new PropertyChangeSupport(this); >- super.dispose(); >- } >- >- /** >- * Gets the graph item type. The item type is one of: GRAPH, NODE or >- * CONNECTION >- * >- * @return >- */ >- public abstract int getItemType(); >- >- /** >- * Set the visibility of this item. >- * >- * @param visible >- * whether or not this item is visible. >- */ >- public abstract void setVisible(boolean visible); >- >- /** >- * Get the visibility of this item. >- * >- * @return the visibility of this item. >- */ >- public abstract boolean isVisible(); >- >- /** >- * Gets the graph that this item is rooted on. If this item is itself a >- * graph, then this is returned. >- * >- * @return the parent graph. >- */ >- public abstract Graph getGraphModel(); >- >- /** >- * Highlights the current GraphItem. A graph item is either a graph node or >- * graph connection, and highlighting them will set the appropriate highlight >- * color. >- */ >- public abstract void highlight(); >- >- /** >- * Unhighlight sets the graphItem (either a graphNode or graphConnection) back >- * to the unhighlight figure or color. >- */ >- public abstract void unhighlight(); >- >- abstract IFigure getFigure(); >- >- /** >- * Checks a style to see if it is set on the given graph item >- * @param styleToCheck The style to check >- * @return >- */ >- protected boolean checkStyle(int styleToCheck) { >- return ((getStyle() & styleToCheck) > 0); >- } >-} >+/******************************************************************************* >+ * Copyright 2005-2010, 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 >+ * Mateusz Matela >+ ******************************************************************************/ >+package org.eclipse.zest.core.widgets; >+ >+import org.eclipse.draw2d.IFigure; >+import org.eclipse.swt.SWT; >+import org.eclipse.swt.widgets.Item; >+import org.eclipse.swt.widgets.Widget; >+ >+/** >+ * Provides support for property changes. All model elements extend this class. >+ * Also extends the Item (Widget) class to be used inside a StructuredViewer. >+ * >+ * @author Chris Callendar >+ */ >+public abstract class GraphItem extends Item { >+ >+ public static final int GRAPH = 0; >+ public static final int NODE = 1; >+ public static final int CONNECTION = 2; >+ public static final int CONTAINER = 3; >+ >+ /** >+ * @param parent >+ * @param style >+ */ >+ public GraphItem(Widget parent, int style) { >+ this(parent, style | SWT.NO_BACKGROUND, null); >+ } >+ >+ /** >+ * @param parent >+ * @param style >+ */ >+ public GraphItem(Widget parent, int style, Object data) { >+ super(parent, style | SWT.NO_BACKGROUND); >+ if (data != null) { >+ this.setData(data); >+ } >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see org.eclipse.swt.widgets.Widget#dispose() >+ */ >+ public void dispose() { >+ // @tag zest.bug.167132-ListenerDispose : remove all listeners. >+ // pcsDelegate = new PropertyChangeSupport(this); >+ super.dispose(); >+ } >+ >+ /** >+ * Gets the graph item type. The item type is one of: GRAPH, NODE or >+ * CONNECTION >+ * >+ * @return >+ */ >+ public abstract int getItemType(); >+ >+ /** >+ * Set the visibility of this item. >+ * >+ * @param visible >+ * whether or not this item is visible. >+ */ >+ public abstract void setVisible(boolean visible); >+ >+ /** >+ * Get the visibility of this item. >+ * >+ * @return the visibility of this item. >+ */ >+ public abstract boolean isVisible(); >+ >+ /** >+ * Gets the graph that this item is rooted on. If this item is itself a >+ * graph, then this is returned. >+ * >+ * @return the parent graph. >+ */ >+ public abstract Graph getGraphModel(); >+ >+ /** >+ * Highlights the current GraphItem. A graph item is either a graph node or >+ * graph connection, and highlighting them will set the appropriate >+ * highlight color. >+ */ >+ public abstract void highlight(); >+ >+ /** >+ * Unhighlight sets the graphItem (either a graphNode or graphConnection) >+ * back to the unhighlight figure or color. >+ */ >+ public abstract void unhighlight(); >+ >+ abstract IFigure getFigure(); >+ >+ /** >+ * Checks a style to see if it is set on the given graph item >+ * >+ * @param styleToCheck >+ * The style to check >+ * @return >+ */ >+ protected boolean checkStyle(int styleToCheck) { >+ return ((getStyle() & styleToCheck) > 0); >+ } >+} >Index: src/org/eclipse/zest/core/widgets/GraphNode.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/widgets/GraphNode.java,v >retrieving revision 1.40 >diff -u -r1.40 GraphNode.java >--- src/org/eclipse/zest/core/widgets/GraphNode.java 23 Apr 2009 02:41:22 -0000 1.40 >+++ src/org/eclipse/zest/core/widgets/GraphNode.java 15 Mar 2010 03:48:58 -0000 >@@ -1,955 +1,789 @@ >-/******************************************************************************* >- * Copyright 2005, 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.widgets; >- >-import java.util.ArrayList; >-import java.util.Iterator; >-import java.util.List; >- >-import org.eclipse.draw2d.ColorConstants; >-import org.eclipse.draw2d.IFigure; >-import org.eclipse.draw2d.Label; >-import org.eclipse.draw2d.geometry.Dimension; >-import org.eclipse.draw2d.geometry.Insets; >-import org.eclipse.draw2d.geometry.Point; >-import org.eclipse.draw2d.geometry.PrecisionPoint; >-import org.eclipse.draw2d.geometry.Rectangle; >-import org.eclipse.swt.SWT; >-import org.eclipse.swt.graphics.Color; >-import org.eclipse.swt.graphics.Font; >-import org.eclipse.swt.graphics.FontData; >-import org.eclipse.swt.graphics.Image; >-import org.eclipse.swt.widgets.Display; >-import org.eclipse.zest.core.widgets.internal.GraphLabel; >-import org.eclipse.zest.layouts.LayoutEntity; >-import org.eclipse.zest.layouts.constraints.LayoutConstraint; >- >-/* >- * Simple node class which has the following properties: color, size, location, >- * and a label. It also has a list of connections and anchors. >- * >- * @author Chris Callendar >- * >- * @author Del Myers >- * >- * @author Ian Bull >- */ >-public class GraphNode extends GraphItem { >- public static final int HIGHLIGHT_NONE = 0; >- public static final int HIGHLIGHT_ON = 1; >- // @tag ADJACENT : Removed highlight adjacent >- //public static final int HIGHLIGHT_ADJACENT = 2; >- >- private int nodeStyle; >- >- private List /* IGraphModelConnection */sourceConnections; >- private List /* IGraphModelConnection */targetConnections; >- >- private Color foreColor; >- private Color backColor; >- private Color highlightColor; >- // @tag ADJACENT : Removed highlight adjacent >- //private Color highlightAdjacentColor; >- private Color borderColor; >- private Color borderHighlightColor; >- private int borderWidth; >- private Point currentLocation; >- protected Dimension size; >- private Font font; >- private boolean cacheLabel; >- private boolean visible = true; >- private LayoutEntity layoutEntity; >- >- protected Graph graph; >- protected IContainer parent; >- >- /** The internal node. */ >- protected Object internalNode; >- private boolean selected; >- protected int highlighted = HIGHLIGHT_NONE; >- private IFigure tooltip; >- protected IFigure nodeFigure; >- >- private boolean isDisposed = false; >- private boolean hasCustomTooltip; >- >- public GraphNode(IContainer graphModel, int style) { >- this(graphModel, style, null); >- } >- >- public GraphNode(IContainer graphModel, int style, Object data) { >- this(graphModel.getGraph(), style, "" /*text*/, null /*image*/, data); >- } >- >- public GraphNode(IContainer graphModel, int style, String text) { >- this(graphModel, style, text, null); >- } >- >- public GraphNode(IContainer graphModel, int style, String text, Object data) { >- this(graphModel.getGraph(), style, text, null /*image*/, data); >- } >- >- public GraphNode(IContainer graphModel, int style, String text, Image image) { >- this(graphModel, style, text, image, null); >- } >- >- public GraphNode(IContainer graphModel, int style, String text, Image image, Object data) { >- super(graphModel.getGraph(), style, data); >- initModel(graphModel, text, image); >- if (nodeFigure == null) { >- initFigure(); >- } >- >- // This is a hack because JAVA sucks! >- // I don't want to expose addNode so I can't put it in the >- // IContainer interface. >- if (this.parent.getItemType() == GRAPH) { >- ((Graph) this.parent).addNode(this); >- } else if (this.parent.getItemType() == CONTAINER) { >- ((GraphContainer) this.parent).addNode(this); >- } >- this.parent.getGraph().registerItem(this); >- } >- >- protected void initFigure() { >- nodeFigure = createFigureForModel(); >- } >- >- static int count = 0; >- >- protected void initModel(IContainer parent, String text, Image image) { >- this.nodeStyle |= parent.getGraph().getNodeStyle(); >- this.parent = parent; >- this.sourceConnections = new ArrayList(); >- this.targetConnections = new ArrayList(); >- this.foreColor = parent.getGraph().DARK_BLUE; >- this.backColor = parent.getGraph().LIGHT_BLUE; >- this.highlightColor = parent.getGraph().HIGHLIGHT_COLOR; >- // @tag ADJACENT : Removed highlight adjacent >- //this.highlightAdjacentColor = ColorConstants.orange; >- this.nodeStyle = SWT.NONE; >- this.borderColor = ColorConstants.lightGray; >- this.borderHighlightColor = ColorConstants.blue; >- this.borderWidth = 1; >- this.currentLocation = new PrecisionPoint(0, 0); >- this.size = new Dimension(-1, -1); >- this.font = Display.getDefault().getSystemFont(); >- this.graph = parent.getGraph(); >- this.cacheLabel = false; >- this.setText(text); >- this.layoutEntity = new LayoutGraphNode(); >- if (image != null) { >- this.setImage(image); >- } >- >- if (font == null) { >- font = Display.getDefault().getSystemFont(); >- } >- >- } >- >- /** >- * A simple toString that we can use for debugging >- */ >- public String toString() { >- return "GraphModelNode: " + getText(); >- } >- >- public LayoutEntity getLayoutEntity() { >- return layoutEntity; >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.mylar.zest.core.widgets.GraphItem#dispose() >- */ >- public void dispose() { >- if (isFisheyeEnabled) { >- this.fishEye(false, false); >- } >- super.dispose(); >- this.isDisposed = true; >- while (getSourceConnections().size() > 0) { >- GraphConnection connection = (GraphConnection) getSourceConnections().get(0); >- if (!connection.isDisposed()) { >- connection.dispose(); >- } else { >- removeSourceConnection(connection); >- } >- } >- while (getTargetConnections().size() > 0) { >- GraphConnection connection = (GraphConnection) getTargetConnections().get(0); >- if (!connection.isDisposed()) { >- connection.dispose(); >- } else { >- removeTargetConnection(connection); >- } >- } >- graph.removeNode(this); >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.swt.widgets.Widget#isDisposed() >- */ >- public boolean isDisposed() { >- return isDisposed; >- } >- >- /** >- * Determines if this node has a fixed size or if it is packed to the size of its contents. >- * To set a node to pack, set its size (-1, -1) >- * @return >- */ >- public boolean isSizeFixed() { >- return !(this.size.width < 0 && this.size.height < 0); >- } >- >- /** >- * Returns a new list of the source connections (GraphModelConnection >- * objects). >- * >- * @return List a new list of GraphModelConnect objects >- */ >- public List getSourceConnections() { >- return new ArrayList(sourceConnections); >- } >- >- /** >- * Returns a new list of the target connections (GraphModelConnection >- * objects). >- * >- * @return List a new list of GraphModelConnect objects >- */ >- public List getTargetConnections() { >- return new ArrayList(targetConnections); >- } >- >- /** >- * Returns the bounds of this node. It is just the combination of the >- * location and the size. >- * >- * @return Rectangle >- */ >- Rectangle getBounds() { >- return new Rectangle(getLocation(), getSize()); >- } >- >- /** >- * Returns a copy of the node's location. >- * >- * @return Point >- */ >- public Point getLocation() { >- return currentLocation; >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#isSelected() >- */ >- public boolean isSelected() { >- return selected; >- } >- >- /** >- * Sets the current location for this node. >- */ >- public void setLocation(double x, double y) { >- currentLocation.x = (int) x; >- currentLocation.y = (int) y; >- refreshLocation(); >- } >- >- /** >- * Returns a copy of the node's size. >- * >- * @return Dimension >- */ >- public Dimension getSize() { >- if (size.height < 0 && size.width < 0 && nodeFigure != null) { >- return nodeFigure.getSize().getCopy(); >- } >- return size.getCopy(); >- } >- >- /** >- * Get the foreground colour for this node >- */ >- public Color getForegroundColor() { >- return foreColor; >- } >- >- /** >- * Set the foreground colour for this node >- */ >- public void setForegroundColor(Color c) { >- this.foreColor = c; >- updateFigureForModel(nodeFigure); >- } >- >- /** >- * Get the background colour for this node. This is the color the node will >- * be if it is not currently highlighted. This color is meaningless if a >- * custom figure has been set. >- */ >- public Color getBackgroundColor() { >- return backColor; >- } >- >- /** >- * Permanently sets the background color (unhighlighted). This color has no >- * effect if a custom figure has been set. >- * >- * @param c >- */ >- public void setBackgroundColor(Color c) { >- backColor = c; >- updateFigureForModel(nodeFigure); >- } >- >- /** >- * Sets the tooltip on this node. This tooltip will display if the mouse >- * hovers over the node. Setting the tooltip has no effect if a custom >- * figure has been set. >- */ >- public void setTooltip(IFigure tooltip) { >- hasCustomTooltip = true; >- this.tooltip = tooltip; >- updateFigureForModel(nodeFigure); >- } >- >- /** >- * Gets the current tooltip for this node. The tooltip returned is >- * meaningless if a custom figure has been set. >- */ >- public IFigure getTooltip() { >- return this.tooltip; >- } >- >- /** >- * Sets the border color. >- * >- * @param c >- * the border color. >- */ >- public void setBorderColor(Color c) { >- borderColor = c; >- updateFigureForModel(nodeFigure); >- } >- >- /** >- * Sets the highlighted border color. >- * >- * @param c >- * the highlighted border color. >- */ >- public void setBorderHighlightColor(Color c) { >- this.borderHighlightColor = c; >- updateFigureForModel(nodeFigure); >- } >- >- /** >- * Get the highlight colour for this node >- */ >- public Color getHighlightColor() { >- return highlightColor; >- } >- >- /** >- * Set the highlight colour for this node >- */ >- public void setHighlightColor(Color c) { >- this.highlightColor = c; >- } >- >- /** >- * Get the highlight adjacent colour for this node. This is the colour that >- * adjacent nodes will get >- */ >- // @tag ADJACENT : Removed highlight adjacent >- /* >- public Color getHighlightAdjacentColor() { >- return highlightAdjacentColor; >- } >- */ >- >- /** >- * Set the highlight adjacent colour for this node. This is the colour that >- * adjacent node will get. >- */ >- // @tag ADJACENT : Removed highlight adjacent >- /* >- public void setHighlightAdjacentColor(Color c) { >- this.highlightAdjacentColor = c; >- } >- */ >- >- /** >- * Highlights the node changing the background color and border color. The >- * source and destination connections are also highlighted, and the adjacent >- * nodes are highlighted too in a different color. >- */ >- public void highlight() { >- if (highlighted == HIGHLIGHT_ON) { >- return; >- } >- // @tag ADJACENT : Removed highlight adjacent >- /* >- if (ZestStyles.checkStyle(getNodeStyle(), ZestStyles.NODES_HIGHLIGHT_ADJACENT)) { >- for (Iterator iter = sourceConnections.iterator(); iter.hasNext();) { >- GraphConnection conn = (GraphConnection) iter.next(); >- conn.highlight(); >- conn.getDestination().highlightAdjacent(); >- } >- for (Iterator iter = targetConnections.iterator(); iter.hasNext();) { >- GraphConnection conn = (GraphConnection) iter.next(); >- conn.highlight(); >- conn.getSource().highlightAdjacent(); >- } >- } >- */ >- if (parent.getItemType() == GraphItem.CONTAINER) { >- ((GraphContainer) parent).highlightNode(this); >- } else { >- ((Graph) parent).highlightNode(this); >- } >- highlighted = HIGHLIGHT_ON; >- updateFigureForModel(getNodeFigure()); >- } >- >- /** >- * Restores the nodes original background color and border width. >- */ >- public void unhighlight() { >- >- // @tag ADJACENT : Removed highlight adjacent >- //boolean highlightedAdjacently = (highlighted == HIGHLIGHT_ADJACENT); >- if (highlighted == HIGHLIGHT_NONE) { >- return; >- } >- // @tag ADJACENT : Removed highlight adjacent >- /* >- if (!highlightedAdjacently) { >- // IF we are highlighted as an adjacent node, we don't need to deal >- // with our connections. >- if (ZestStyles.checkStyle(getNodeStyle(), ZestStyles.NODES_HIGHLIGHT_ADJACENT)) { >- // unhighlight the adjacent edges >- for (Iterator iter = sourceConnections.iterator(); iter.hasNext();) { >- GraphConnection conn = (GraphConnection) iter.next(); >- conn.unhighlight(); >- if (conn.getDestination() != this) { >- conn.getDestination().unhighlight(); >- } >- } >- for (Iterator iter = targetConnections.iterator(); iter.hasNext();) { >- GraphConnection conn = (GraphConnection) iter.next(); >- conn.unhighlight(); >- if (conn.getSource() != this) { >- conn.getSource().unhighlight(); >- } >- } >- } >- } >- */ >- if (parent.getItemType() == GraphItem.CONTAINER) { >- ((GraphContainer) parent).unhighlightNode(this); >- } else { >- ((Graph) parent).unhighlightNode(this); >- } >- highlighted = HIGHLIGHT_NONE; >- updateFigureForModel(nodeFigure); >- >- } >- >- protected void refreshLocation() { >- Point loc = this.getLocation(); >- Dimension size = this.getSize(); >- Rectangle bounds = new Rectangle(loc, size); >- >- if (nodeFigure == null || nodeFigure.getParent() == null) { >- return; // node figure has not been created yet >- } >- //nodeFigure.setBounds(bounds); >- nodeFigure.getParent().setConstraint(nodeFigure, bounds); >- } >- >- /** >- * Highlights this node using the adjacent highlight color. This only does >- * something if highlighAdjacentNodes is set to true and if the node isn't >- * already highlighted. >- * >- * @see #setHighlightAdjacentNodes(boolean) >- */ >- // @tag ADJACENT : removed highlight adjacent >- /* >- public void highlightAdjacent() { >- if (highlighted > 0) { >- return; >- } >- highlighted = HIGHLIGHT_ADJACENT; >- updateFigureForModel(nodeFigure); >- if (parent.getItemType() == GraphItem.CONTAINER) { >- ((GraphContainer) parent).highlightNode(this); >- } else { >- ((Graph) parent).highlightNode(this); >- } >- } >- */ >- >- /** >- * Returns if the nodes adjacent to this node will be highlighted when this >- * node is selected. >- * >- * @return GraphModelNode >- */ >- // @tag ADJACENT : Removed highlight adjacent >- /* >- public boolean isHighlightAdjacentNodes() { >- return ZestStyles.checkStyle(nodeStyle, ZestStyles.NODES_HIGHLIGHT_ADJACENT); >- } >- */ >- >- /** >- * Sets if the adjacent nodes to this one should be highlighted when this >- * node is selected. >- * >- * @param highlightAdjacentNodes >- * The highlightAdjacentNodes to set. >- */ >- // @tag ADJACENT : Removed highlight adjacent >- /* >- public void setHighlightAdjacentNodes(boolean highlightAdjacentNodes) { >- if (!highlightAdjacentNodes) { >- this.nodeStyle |= ZestStyles.NODES_HIGHLIGHT_ADJACENT; >- this.nodeStyle ^= ZestStyles.NODES_HIGHLIGHT_ADJACENT; >- return; >- } >- this.nodeStyle |= ZestStyles.NODES_HIGHLIGHT_ADJACENT; >- } >- */ >- >- public Color getBorderColor() { >- return borderColor; >- } >- >- public int getBorderWidth() { >- return borderWidth; >- } >- >- public void setBorderWidth(int width) { >- this.borderWidth = width; >- updateFigureForModel(nodeFigure); >- } >- >- public Font getFont() { >- return font; >- } >- >- public void setFont(Font font) { >- this.font = font; >- updateFigureForModel(nodeFigure); >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.swt.widgets.Item#setText(java.lang.String) >- */ >- public void setText(String string) { >- if (string == null) { >- string = ""; >- } >- super.setText(string); >- >- if (nodeFigure != null) { >- updateFigureForModel(this.nodeFigure); >- } >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.swt.widgets.Item#setImage(org.eclipse.swt.graphics.Image) >- */ >- public void setImage(Image image) { >- super.setImage(image); >- if (nodeFigure != null) { >- updateFigureForModel(nodeFigure); >- } >- } >- >- /** >- * Gets the graphModel that this node is contained in >- * >- * @return The graph model that this node is contained in >- */ >- public Graph getGraphModel() { >- return this.graph; >- } >- >- /** >- * @return the nodeStyle >- */ >- public int getNodeStyle() { >- return nodeStyle; >- } >- >- /** >- * @param nodeStyle >- * the nodeStyle to set >- */ >- public void setNodeStyle(int nodeStyle) { >- this.nodeStyle = nodeStyle; >- this.cacheLabel = ((this.nodeStyle & ZestStyles.NODES_CACHE_LABEL) > 0) ? true : false; >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#setSize(double, >- * double) >- */ >- public void setSize(double width, double height) { >- if ((width != size.width) || (height != size.height)) { >- size.width = (int) width; >- size.height = (int) height; >- refreshLocation(); >- } >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#getBorderHighlightColor() >- */ >- public Color getBorderHighlightColor() { >- return borderHighlightColor; >- } >- >- public boolean cacheLabel() { >- return this.cacheLabel; >- } >- >- public void setCacheLabel(boolean cacheLabel) { >- this.cacheLabel = cacheLabel; >- } >- >- public IFigure getNodeFigure() { >- return this.nodeFigure; >- } >- >- public void setVisible(boolean visible) { >- // graph.addRemoveFigure(this, visible); >- this.visible = visible; >- this.getFigure().setVisible(visible); >- List sConnections = (this).getSourceConnections(); >- List tConnections = (this).getTargetConnections(); >- for (Iterator iterator2 = sConnections.iterator(); iterator2.hasNext();) { >- GraphConnection connection = (GraphConnection) iterator2.next(); >- connection.setVisible(visible); >- } >- >- for (Iterator iterator2 = tConnections.iterator(); iterator2.hasNext();) { >- GraphConnection connection = (GraphConnection) iterator2.next(); >- connection.setVisible(visible); >- } >- } >- >- public int getStyle() { >- return super.getStyle() | this.getNodeStyle(); >- } >- >- /*************************************************************************** >- * PRIVATE MEMBERS >- **************************************************************************/ >- >- private IFigure fishEyeFigure = null; >- private Font fishEyeFont = null; >- private boolean isFisheyeEnabled; >- >- protected IFigure fishEye(boolean enable, boolean animate) { >- if (isDisposed) { >- // If a fisheyed figure is still left on the canvas, we could get >- // called once more after the dispose is called. Since we cleaned >- // up everything on dispose, we can just return null here. >- return null; >- } >- if (!checkStyle(ZestStyles.NODES_FISHEYE)) { >- return null; >- } >- if (enable) { >- // Create the fish eye label >- fishEyeFigure = createFishEyeFigure(); >- >- // Get the current Bounds >- Rectangle rectangle = nodeFigure.getBounds().getCopy(); >- >- // Calculate how much we have to expand the current bounds to get to the new bounds >- Dimension newSize = fishEyeFigure.getPreferredSize(); >- Rectangle currentSize = rectangle.getCopy(); >- nodeFigure.translateToAbsolute(currentSize); >- int expandedH = (newSize.height - currentSize.height) / 2 + 1; >- int expandedW = (newSize.width - currentSize.width) / 2 + 1; >- Dimension expandAmount = new Dimension(expandedW, expandedH); >- nodeFigure.translateToAbsolute(rectangle); >- rectangle.expand(new Insets(expandAmount.height, expandAmount.width, expandAmount.height, expandAmount.width)); >- if (expandedH <= 0 && expandedW <= 0) { >- return null; >- } >- >- FontData fontData = Display.getCurrent().getSystemFont().getFontData()[0]; >- fontData.height = 12; >- fishEyeFont = new Font(Display.getCurrent(), fontData); >- fishEyeFigure.setFont(fishEyeFont); >- >- //Add the fisheye >- this.getGraphModel().fishEye(nodeFigure, fishEyeFigure, rectangle, true); >- if (fishEyeFigure != null) { >- isFisheyeEnabled = true; >- } >- return fishEyeFigure; >- >- } else { >- // Remove the fisheye and dispose the font >- this.getGraphModel().removeFishEye(fishEyeFigure, nodeFigure, animate); >- if (fishEyeFont != null) { >- this.fishEyeFont.dispose(); >- this.fishEyeFont = null; >- } >- isFisheyeEnabled = false; >- return null; >- } >- } >- >- IContainer getParent() { >- return parent; >- } >- >- boolean isHighlighted() { >- return highlighted > 0; >- } >- >- void invokeLayoutListeners(LayoutConstraint constraint) { >- graph.invokeConstraintAdapters(this, constraint); >- } >- >- protected void updateFigureForModel(IFigure currentFigure) { >- if (currentFigure == null) { >- return; >- } >- >- if (!(currentFigure instanceof GraphLabel)) { >- return; >- } >- GraphLabel figure = (GraphLabel) currentFigure; >- IFigure toolTip; >- >- if (!checkStyle(ZestStyles.NODES_HIDE_TEXT)) { >- figure.setText(this.getText()); >- } >- figure.setIcon(getImage()); >- >- if (highlighted == HIGHLIGHT_ON) { >- figure.setForegroundColor(getForegroundColor()); >- figure.setBackgroundColor(getHighlightColor()); >- figure.setBorderColor(getBorderHighlightColor()); >- } else { >- figure.setForegroundColor(getForegroundColor()); >- figure.setBackgroundColor(getBackgroundColor()); >- figure.setBorderColor(getBorderColor()); >- } >- >- figure.setBorderWidth(getBorderWidth()); >- >- figure.setFont(getFont()); >- >- if (this.getTooltip() == null && hasCustomTooltip == false) { >- // if we have a custom tooltip, don't try and create our own. >- toolTip = new Label(); >- ((Label) toolTip).setText(getText()); >- } else { >- toolTip = this.getTooltip(); >- } >- figure.setToolTip(toolTip); >- >- refreshLocation(); >- >- if (isFisheyeEnabled) { >- IFigure newFisheyeFigure = createFishEyeFigure(); >- if (graph.replaceFishFigure(this.fishEyeFigure, newFisheyeFigure)) { >- this.fishEyeFigure = newFisheyeFigure; >- } >- } >- } >- >- protected IFigure createFigureForModel() { >- GraphNode node = this; >- boolean cacheLabel = (this).cacheLabel(); >- GraphLabel label = new GraphLabel(node.getText(), node.getImage(), cacheLabel); >- label.setFont(this.font); >- if (checkStyle(ZestStyles.NODES_HIDE_TEXT)) { >- label.setText(""); >- } >- updateFigureForModel(label); >- return label; >- } >- >- private IFigure createFishEyeFigure() { >- GraphNode node = this; >- boolean cacheLabel = this.cacheLabel(); >- GraphLabel label = new GraphLabel(node.getText(), node.getImage(), cacheLabel); >- >- if (!checkStyle(ZestStyles.NODES_HIDE_TEXT)) { >- label.setText(this.getText()); >- } >- label.setIcon(getImage()); >- >- // @tag TODO: Add border and foreground colours to highlight >- // (this.borderColor) >- if (highlighted == HIGHLIGHT_ON) { >- label.setForegroundColor(getForegroundColor()); >- label.setBackgroundColor(getHighlightColor()); >- } else { >- label.setForegroundColor(getForegroundColor()); >- label.setBackgroundColor(getBackgroundColor()); >- } >- >- label.setFont(getFont()); >- return label; >- } >- >- public boolean isVisible() { >- return visible; >- } >- >- void addSourceConnection(GraphConnection connection) { >- this.sourceConnections.add(connection); >- } >- >- void addTargetConnection(GraphConnection connection) { >- this.targetConnections.add(connection); >- } >- >- void removeSourceConnection(GraphConnection connection) { >- this.sourceConnections.remove(connection); >- } >- >- void removeTargetConnection(GraphConnection connection) { >- this.targetConnections.remove(connection); >- } >- >- /** >- * Sets the node as selected. >- */ >- void setSelected(boolean selected) { >- if (selected = isSelected()) { >- return; >- } >- if (selected) { >- highlight(); >- } else { >- unhighlight(); >- } >- this.selected = selected; >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.mylar.zest.core.widgets.IGraphItem#getItemType() >- */ >- public int getItemType() { >- return NODE; >- } >- >- class LayoutGraphNode implements LayoutEntity { >- Object layoutInformation = null; >- >- public double getHeightInLayout() { >- return getSize().height; >- } >- >- public Object getLayoutInformation() { >- return layoutInformation; >- } >- >- public String toString() { >- return getText(); >- } >- >- public double getWidthInLayout() { >- return getSize().width; >- } >- >- public double getXInLayout() { >- return getLocation().x; >- } >- >- public double getYInLayout() { >- return getLocation().y; >- } >- >- public void populateLayoutConstraint(LayoutConstraint constraint) { >- invokeLayoutListeners(constraint); >- } >- >- public void setLayoutInformation(Object internalEntity) { >- this.layoutInformation = internalEntity; >- >- } >- >- public void setLocationInLayout(double x, double y) { >- setLocation(x, y); >- } >- >- public void setSizeInLayout(double width, double height) { >- setSize(width, height); >- } >- >- /** >- * Compares two nodes. >- */ >- public int compareTo(Object otherNode) { >- int rv = 0; >- if (otherNode instanceof GraphNode) { >- GraphNode node = (GraphNode) otherNode; >- if (getText() != null) { >- rv = getText().compareTo(node.getText()); >- } >- } >- return rv; >- } >- >- public Object getGraphData() { >- return GraphNode.this; >- } >- >- public void setGraphData(Object o) { >- // TODO Auto-generated method stub >- >- } >- >- } >- >- IFigure getFigure() { >- if (this.nodeFigure == null) { >- initFigure(); >- } >- return this.getNodeFigure(); >- } >- >- void paint() { >- >- } >-} >+/******************************************************************************* >+ * Copyright 2005-2010, 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 >+ * Mateusz Matela >+ ******************************************************************************/ >+package org.eclipse.zest.core.widgets; >+ >+import java.util.ArrayList; >+import java.util.Iterator; >+import java.util.List; >+ >+import org.eclipse.draw2d.Animation; >+import org.eclipse.draw2d.ColorConstants; >+import org.eclipse.draw2d.FigureListener; >+import org.eclipse.draw2d.IFigure; >+import org.eclipse.draw2d.Label; >+import org.eclipse.draw2d.geometry.Dimension; >+import org.eclipse.draw2d.geometry.Insets; >+import org.eclipse.draw2d.geometry.Point; >+import org.eclipse.draw2d.geometry.PrecisionPoint; >+import org.eclipse.draw2d.geometry.Rectangle; >+import org.eclipse.swt.graphics.Color; >+import org.eclipse.swt.graphics.Font; >+import org.eclipse.swt.graphics.Image; >+import org.eclipse.swt.widgets.Display; >+import org.eclipse.zest.core.widgets.internal.GraphLabel; >+import org.eclipse.zest.core.widgets.internal.ZestRootLayer; >+ >+/** >+ * Simple node class which has the following properties: color, size, location, >+ * and a label. It also has a list of connections and anchors. >+ * >+ * @author Chris Callendar >+ * >+ * @author Del Myers >+ * >+ * @author Ian Bull >+ */ >+public class GraphNode extends GraphItem { >+ public static final int HIGHLIGHT_NONE = 0; >+ public static final int HIGHLIGHT_ON = 1; >+ >+ private int nodeStyle; >+ >+ private List /* IGraphModelConnection */sourceConnections; >+ private List /* IGraphModelConnection */targetConnections; >+ >+ private Color foreColor; >+ private Color backColor; >+ private Color highlightColor; >+ private Color borderColor; >+ private Color borderHighlightColor; >+ private int borderWidth; >+ private PrecisionPoint currentLocation; >+ protected Dimension size; >+ private Font font; >+ private boolean cacheLabel; >+ private boolean visible = true; >+ >+ protected Graph graph; >+ protected IContainer parent; >+ >+ /** The internal node. */ >+ protected Object internalNode; >+ private boolean selected; >+ protected int highlighted = HIGHLIGHT_NONE; >+ private IFigure tooltip; >+ protected IFigure nodeFigure; >+ >+ private boolean isDisposed = false; >+ private boolean hasCustomTooltip; >+ >+ public GraphNode(IContainer graphModel, int style) { >+ this(graphModel, style, null); >+ } >+ >+ public GraphNode(IContainer graphModel, int style, String text) { >+ this(graphModel, style, text, null, null); >+ } >+ >+ public GraphNode(IContainer graphModel, int style, Object data) { >+ this(graphModel, style, "" /* text */, null /* image */, data); >+ } >+ >+ private GraphNode(IContainer graphModel, int style, String text, >+ Image image, Object data) { >+ super(graphModel.getGraph(), style, data); >+ initModel(graphModel, text, image); >+ if (nodeFigure == null) { >+ initFigure(); >+ } >+ >+ this.parent.addNode(this); >+ this.parent.getGraph().registerItem(this); >+ } >+ >+ protected void initFigure() { >+ nodeFigure = createFigureForModel(); >+ } >+ >+ static int count = 0; >+ >+ protected void initModel(IContainer graphModel, String text, Image image) { >+ this.nodeStyle |= graphModel.getGraph().getNodeStyle(); >+ this.parent = graphModel; >+ this.sourceConnections = new ArrayList(); >+ this.targetConnections = new ArrayList(); >+ this.foreColor = graphModel.getGraph().DARK_BLUE; >+ this.backColor = graphModel.getGraph().LIGHT_BLUE; >+ this.highlightColor = graphModel.getGraph().HIGHLIGHT_COLOR; >+ this.borderColor = ColorConstants.lightGray; >+ this.borderHighlightColor = ColorConstants.blue; >+ this.borderWidth = 1; >+ this.currentLocation = new PrecisionPoint(0, 0); >+ this.size = new Dimension(-1, -1); >+ this.font = Display.getDefault().getSystemFont(); >+ this.graph = graphModel.getGraph(); >+ this.cacheLabel = false; >+ this.setText(text); >+ if (image != null) { >+ this.setImage(image); >+ } >+ >+ if (font == null) { >+ font = Display.getDefault().getSystemFont(); >+ } >+ >+ } >+ >+ /** >+ * A simple toString that we can use for debugging >+ */ >+ public String toString() { >+ return "GraphModelNode: " + getText(); >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see org.eclipse.mylar.zest.core.widgets.GraphItem#dispose() >+ */ >+ public void dispose() { >+ if (isFisheyeEnabled) { >+ this.fishEye(false, false); >+ } >+ super.dispose(); >+ this.isDisposed = true; >+ while (getSourceConnections().size() > 0) { >+ GraphConnection connection = (GraphConnection) getSourceConnections() >+ .get(0); >+ if (!connection.isDisposed()) { >+ connection.dispose(); >+ } else { >+ removeSourceConnection(connection); >+ } >+ } >+ while (getTargetConnections().size() > 0) { >+ GraphConnection connection = (GraphConnection) getTargetConnections() >+ .get(0); >+ if (!connection.isDisposed()) { >+ connection.dispose(); >+ } else { >+ removeTargetConnection(connection); >+ } >+ } >+ graph.removeNode(this); >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see org.eclipse.swt.widgets.Widget#isDisposed() >+ */ >+ public boolean isDisposed() { >+ return isDisposed; >+ } >+ >+ /** >+ * Determines if this node has a fixed size or if it is packed to the size >+ * of its contents. To set a node to pack, set its size (-1, -1) >+ * >+ * @return >+ */ >+ public boolean isSizeFixed() { >+ return !(this.size.width < 0 && this.size.height < 0); >+ } >+ >+ /** >+ * Returns a new list of the source connections (GraphModelConnection >+ * objects). >+ * >+ * @return List a new list of GraphModelConnect objects >+ */ >+ public List getSourceConnections() { >+ return new ArrayList(sourceConnections); >+ } >+ >+ /** >+ * Returns a new list of the target connections (GraphModelConnection >+ * objects). >+ * >+ * @return List a new list of GraphModelConnect objects >+ */ >+ public List getTargetConnections() { >+ return new ArrayList(targetConnections); >+ } >+ >+ /** >+ * Returns the bounds of this node. It is just the combination of the >+ * location and the size. >+ * >+ * @return Rectangle >+ */ >+ Rectangle getBounds() { >+ return new Rectangle(getLocation(), getSize()); >+ } >+ >+ /** >+ * Returns a copy of the node's location. >+ * >+ * @return Point >+ */ >+ public Point getLocation() { >+ return currentLocation; >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see >+ * org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#isSelected >+ * () >+ */ >+ boolean isSelected() { >+ return selected; >+ } >+ >+ /** >+ * Sets the current location for this node. >+ */ >+ public void setLocation(double x, double y) { >+ if (currentLocation.preciseX != x || currentLocation.preciseY != y) { >+ currentLocation.preciseX = x; >+ currentLocation.preciseY = y; >+ currentLocation.updateInts(); >+ refreshBounds(); >+ parent.getLayoutContext().fireNodeMovedEvent(this.getLayout()); >+ } >+ } >+ >+ /** >+ * Returns a copy of the node's size. >+ * >+ * @return Dimension >+ */ >+ public Dimension getSize() { >+ if (size.height < 0 && size.width < 0 && nodeFigure != null) { >+ return nodeFigure.getSize().getCopy(); >+ } >+ return size.getCopy(); >+ } >+ >+ /** >+ * Get the foreground colour for this node >+ */ >+ public Color getForegroundColor() { >+ return foreColor; >+ } >+ >+ /** >+ * Set the foreground colour for this node >+ */ >+ public void setForegroundColor(Color c) { >+ this.foreColor = c; >+ updateFigureForModel(nodeFigure); >+ } >+ >+ /** >+ * Get the background colour for this node. This is the color the node will >+ * be if it is not currently highlighted. This color is meaningless if a >+ * custom figure has been set. >+ */ >+ public Color getBackgroundColor() { >+ return backColor; >+ } >+ >+ /** >+ * Permanently sets the background color (unhighlighted). This color has no >+ * effect if a custom figure has been set. >+ * >+ * @param c >+ */ >+ public void setBackgroundColor(Color c) { >+ backColor = c; >+ updateFigureForModel(nodeFigure); >+ } >+ >+ /** >+ * Sets the tooltip on this node. This tooltip will display if the mouse >+ * hovers over the node. Setting the tooltip has no effect if a custom >+ * figure has been set. >+ */ >+ public void setTooltip(IFigure tooltip) { >+ hasCustomTooltip = true; >+ this.tooltip = tooltip; >+ updateFigureForModel(nodeFigure); >+ } >+ >+ /** >+ * Gets the current tooltip for this node. The tooltip returned is >+ * meaningless if a custom figure has been set. >+ */ >+ public IFigure getTooltip() { >+ return this.tooltip; >+ } >+ >+ /** >+ * Sets the border color. >+ * >+ * @param c >+ * the border color. >+ */ >+ public void setBorderColor(Color c) { >+ borderColor = c; >+ updateFigureForModel(nodeFigure); >+ } >+ >+ /** >+ * Sets the highlighted border color. >+ * >+ * @param c >+ * the highlighted border color. >+ */ >+ public void setBorderHighlightColor(Color c) { >+ this.borderHighlightColor = c; >+ updateFigureForModel(nodeFigure); >+ } >+ >+ /** >+ * Get the highlight colour for this node >+ */ >+ public Color getHighlightColor() { >+ return highlightColor; >+ } >+ >+ /** >+ * Set the highlight colour for this node >+ */ >+ public void setHighlightColor(Color c) { >+ this.highlightColor = c; >+ } >+ >+ /** >+ * Highlights the node changing the background color and border color. The >+ * source and destination connections are also highlighted, and the adjacent >+ * nodes are highlighted too in a different color. >+ */ >+ public void highlight() { >+ if (highlighted == HIGHLIGHT_ON) { >+ return; >+ } >+ IFigure parentFigure = nodeFigure.getParent(); >+ if (parentFigure instanceof ZestRootLayer) { >+ ((ZestRootLayer) parentFigure).highlightNode(nodeFigure); >+ } >+ highlighted = HIGHLIGHT_ON; >+ updateFigureForModel(getNodeFigure()); >+ } >+ >+ /** >+ * Restores the nodes original background color and border width. >+ */ >+ public void unhighlight() { >+ >+ if (highlighted == HIGHLIGHT_NONE) { >+ return; >+ } >+ >+ IFigure parentFigure = nodeFigure.getParent(); >+ if (parentFigure instanceof ZestRootLayer) { >+ ((ZestRootLayer) parentFigure).unHighlightNode(nodeFigure); >+ } >+ >+ highlighted = HIGHLIGHT_NONE; >+ updateFigureForModel(nodeFigure); >+ >+ } >+ >+ void refreshBounds() { >+ Point loc = this.getLocation(); >+ Dimension size = this.getSize(); >+ Rectangle bounds = new Rectangle(loc, size); >+ >+ if (nodeFigure == null || nodeFigure.getParent() == null) { >+ return; // node figure has not been created yet >+ } >+ nodeFigure.getParent().setConstraint(nodeFigure, bounds); >+ >+ if (isFisheyeEnabled) { >+ Rectangle fishEyeBounds = calculateFishEyeBounds(); >+ if (fishEyeBounds != null) { >+ fishEyeFigure.getParent().translateToRelative(fishEyeBounds); >+ fishEyeFigure.getParent().translateFromParent(fishEyeBounds); >+ fishEyeFigure.getParent().setConstraint(fishEyeFigure, >+ fishEyeBounds); >+ } >+ } >+ } >+ >+ public Color getBorderColor() { >+ return borderColor; >+ } >+ >+ public int getBorderWidth() { >+ return borderWidth; >+ } >+ >+ public void setBorderWidth(int width) { >+ this.borderWidth = width; >+ updateFigureForModel(nodeFigure); >+ } >+ >+ public Font getFont() { >+ return font; >+ } >+ >+ public void setFont(Font font) { >+ this.font = font; >+ updateFigureForModel(nodeFigure); >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see org.eclipse.swt.widgets.Item#setText(java.lang.String) >+ */ >+ public void setText(String string) { >+ if (string == null) { >+ string = ""; >+ } >+ super.setText(string); >+ >+ updateFigureForModel(this.nodeFigure); >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see >+ * org.eclipse.swt.widgets.Item#setImage(org.eclipse.swt.graphics.Image) >+ */ >+ public void setImage(Image image) { >+ super.setImage(image); >+ updateFigureForModel(nodeFigure); >+ } >+ >+ /** >+ * Gets the graphModel that this node is contained in >+ * >+ * @return The graph model that this node is contained in >+ */ >+ public Graph getGraphModel() { >+ return this.graph; >+ } >+ >+ /** >+ * @return the nodeStyle >+ */ >+ public int getNodeStyle() { >+ return nodeStyle; >+ } >+ >+ /** >+ * @param nodeStyle >+ * the nodeStyle to set >+ */ >+ public void setNodeStyle(int nodeStyle) { >+ this.nodeStyle = nodeStyle; >+ this.cacheLabel = ((this.nodeStyle & ZestStyles.NODES_CACHE_LABEL) > 0) ? true >+ : false; >+ } >+ >+ public void setSize(double width, double height) { >+ if ((width != size.width) || (height != size.height)) { >+ size.width = (int) width; >+ size.height = (int) height; >+ refreshBounds(); >+ } >+ } >+ >+ public Color getBorderHighlightColor() { >+ return borderHighlightColor; >+ } >+ >+ public boolean cacheLabel() { >+ return this.cacheLabel; >+ } >+ >+ public void setCacheLabel(boolean cacheLabel) { >+ this.cacheLabel = cacheLabel; >+ } >+ >+ IFigure getNodeFigure() { >+ return this.nodeFigure; >+ } >+ >+ public void setVisible(boolean visible) { >+ this.visible = visible; >+ this.getFigure().setVisible(visible); >+ for (Iterator iterator2 = sourceConnections.iterator(); iterator2 >+ .hasNext();) { >+ GraphConnection connection = (GraphConnection) iterator2.next(); >+ connection.setVisible(visible); >+ } >+ >+ for (Iterator iterator2 = targetConnections.iterator(); iterator2 >+ .hasNext();) { >+ GraphConnection connection = (GraphConnection) iterator2.next(); >+ connection.setVisible(visible); >+ } >+ } >+ >+ public boolean isVisible() { >+ return visible; >+ } >+ >+ public int getStyle() { >+ return super.getStyle() | this.getNodeStyle(); >+ } >+ >+ /*************************************************************************** >+ * PRIVATE MEMBERS >+ **************************************************************************/ >+ >+ private IFigure fishEyeFigure = null; >+ private boolean isFisheyeEnabled; >+ >+ protected IFigure fishEye(boolean enable, boolean animate) { >+ if (isDisposed) { >+ // If a fisheyed figure is still left on the canvas, we could get >+ // called once more after the dispose is called. Since we cleaned >+ // up everything on dispose, we can just return null here. >+ return null; >+ } >+ if (!checkStyle(ZestStyles.NODES_FISHEYE)) { >+ return null; >+ } >+ if (enable) { >+ // Create the fish eye label >+ fishEyeFigure = createFishEyeFigure(); >+ >+ Rectangle rectangle = calculateFishEyeBounds(); >+ >+ if (rectangle == null) { >+ return null; >+ } >+ >+ // Add the fisheye >+ this.getGraphModel().fishEye(nodeFigure, fishEyeFigure, rectangle, >+ true); >+ if (fishEyeFigure != null) { >+ isFisheyeEnabled = true; >+ } >+ return fishEyeFigure; >+ >+ } else { >+ isFisheyeEnabled = false; >+ this.getGraphModel().removeFishEye(fishEyeFigure, nodeFigure, >+ animate); >+ return null; >+ } >+ } >+ >+ IContainer getParent() { >+ return parent; >+ } >+ >+ boolean isHighlighted() { >+ return highlighted > 0; >+ } >+ >+ protected void updateFigureForModel(IFigure currentFigure) { >+ if (currentFigure == null) { >+ return; >+ } >+ >+ if (!(currentFigure instanceof GraphLabel)) { >+ return; >+ } >+ GraphLabel figure = (GraphLabel) currentFigure; >+ IFigure toolTip; >+ >+ if (!checkStyle(ZestStyles.NODES_HIDE_TEXT) >+ && !figure.getText().equals(this.getText())) { >+ figure.setText(this.getText()); >+ } >+ if (figure.getIcon() != getImage()) { >+ figure.setIcon(getImage()); >+ } >+ >+ if (highlighted == HIGHLIGHT_ON) { >+ figure.setForegroundColor(getForegroundColor()); >+ figure.setBackgroundColor(getHighlightColor()); >+ figure.setBorderColor(getBorderHighlightColor()); >+ } else { >+ figure.setForegroundColor(getForegroundColor()); >+ figure.setBackgroundColor(getBackgroundColor()); >+ figure.setBorderColor(getBorderColor()); >+ } >+ >+ figure.setBorderWidth(getBorderWidth()); >+ >+ if (figure.getFont() != getFont()) { >+ figure.setFont(getFont()); >+ } >+ >+ if (this.getTooltip() == null && hasCustomTooltip == false) { >+ // if we have a custom tooltip, don't try and create our own. >+ toolTip = new Label(); >+ ((Label) toolTip).setText(getText()); >+ } else { >+ toolTip = this.getTooltip(); >+ } >+ figure.setToolTip(toolTip); >+ >+ if (isFisheyeEnabled) { >+ IFigure newFisheyeFigure = createFishEyeFigure(); >+ if (graph.replaceFishFigure(this.fishEyeFigure, newFisheyeFigure)) { >+ this.fishEyeFigure = newFisheyeFigure; >+ } >+ } >+ refreshBounds(); >+ } >+ >+ protected IFigure createFigureForModel() { >+ GraphNode node = this; >+ boolean cacheLabel = (this).cacheLabel(); >+ final GraphLabel label = new GraphLabel(node.getText(), >+ node.getImage(), cacheLabel); >+ label.setFont(this.font); >+ if (checkStyle(ZestStyles.NODES_HIDE_TEXT)) { >+ label.setText(""); >+ } >+ updateFigureForModel(label); >+ label.addFigureListener(new FigureListener() { >+ private Dimension previousSize = label.getBounds().getSize(); >+ >+ public void figureMoved(IFigure source) { >+ if (Animation.isAnimating() || getLayout().isMinimized()) { >+ return; >+ } >+ Rectangle newBounds = nodeFigure.getBounds(); >+ if (!newBounds.getSize().equals(previousSize)) { >+ previousSize = newBounds.getSize(); >+ if (size.width >= 0 && size.height >= 0) { >+ size = newBounds.getSize(); >+ } >+ currentLocation = new PrecisionPoint(nodeFigure.getBounds() >+ .getTopLeft()); >+ parent.getLayoutContext().fireNodeResizedEvent(getLayout()); >+ } else if (currentLocation.x != newBounds.x >+ || currentLocation.y != newBounds.y) { >+ currentLocation = new PrecisionPoint(nodeFigure.getBounds() >+ .getTopLeft()); >+ parent.getLayoutContext().fireNodeMovedEvent(getLayout()); >+ } >+ } >+ }); >+ return label; >+ } >+ >+ private IFigure createFishEyeFigure() { >+ GraphNode node = this; >+ boolean cacheLabel = this.cacheLabel(); >+ GraphLabel label = new GraphLabel(node.getText(), node.getImage(), >+ cacheLabel); >+ >+ if (highlighted == HIGHLIGHT_ON) { >+ label.setForegroundColor(getForegroundColor()); >+ label.setBackgroundColor(getHighlightColor()); >+ label.setBorderColor(getBorderHighlightColor()); >+ } else { >+ label.setForegroundColor(getForegroundColor()); >+ label.setBackgroundColor(getBackgroundColor()); >+ label.setBorderColor(getBorderColor()); >+ } >+ >+ label.setBorderWidth(getBorderWidth()); >+ label.setFont(getFont()); >+ >+ return label; >+ } >+ >+ private Rectangle calculateFishEyeBounds() { >+ // Get the current Bounds >+ Rectangle rectangle = nodeFigure.getBounds().getCopy(); >+ >+ // Calculate how much we have to expand the current bounds to get to the >+ // new bounds >+ Dimension newSize = fishEyeFigure.getPreferredSize(); >+ Rectangle currentSize = rectangle.getCopy(); >+ nodeFigure.translateToAbsolute(currentSize); >+ int expandedH = Math.max((newSize.height - currentSize.height) / 2 + 1, >+ 0); >+ int expandedW = Math >+ .max((newSize.width - currentSize.width) / 2 + 1, 0); >+ Dimension expandAmount = new Dimension(expandedW, expandedH); >+ nodeFigure.translateToAbsolute(rectangle); >+ rectangle.expand(new Insets(expandAmount.height, expandAmount.width, >+ expandAmount.height, expandAmount.width)); >+ if (expandedH <= 0 && expandedW <= 0) { >+ return null; >+ } >+ return rectangle; >+ } >+ >+ void addSourceConnection(GraphConnection connection) { >+ this.sourceConnections.add(connection); >+ } >+ >+ void addTargetConnection(GraphConnection connection) { >+ this.targetConnections.add(connection); >+ } >+ >+ void removeSourceConnection(GraphConnection connection) { >+ this.sourceConnections.remove(connection); >+ } >+ >+ void removeTargetConnection(GraphConnection connection) { >+ this.targetConnections.remove(connection); >+ } >+ >+ /** >+ * Sets the node as selected. >+ */ >+ void setSelected(boolean selected) { >+ if (selected = isSelected()) { >+ return; >+ } >+ if (selected) { >+ highlight(); >+ } else { >+ unhighlight(); >+ } >+ this.selected = selected; >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see org.eclipse.mylar.zest.core.widgets.IGraphItem#getItemType() >+ */ >+ public int getItemType() { >+ return NODE; >+ } >+ >+ /** >+ * @noreference This method is not intended to be referenced by clients. >+ */ >+ public IFigure getFigure() { >+ if (this.nodeFigure == null) { >+ initFigure(); >+ } >+ return this.getNodeFigure(); >+ } >+ >+ private InternalNodeLayout layout; >+ >+ /** >+ * @noreference This method is not intended to be referenced by clients. >+ */ >+ public InternalNodeLayout getLayout() { >+ if (layout == null) { >+ layout = new InternalNodeLayout(this); >+ } >+ return layout; >+ } >+ >+ void applyLayoutChanges() { >+ if (layout != null) { >+ layout.applyLayout(); >+ } >+ } >+} >Index: src/org/eclipse/zest/core/widgets/IContainer.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/widgets/IContainer.java,v >retrieving revision 1.2 >diff -u -r1.2 IContainer.java >--- src/org/eclipse/zest/core/widgets/IContainer.java 12 Sep 2007 20:44:39 -0000 1.2 >+++ src/org/eclipse/zest/core/widgets/IContainer.java 15 Mar 2010 03:48:58 -0000 >@@ -1,64 +1,93 @@ >-/******************************************************************************* >- * Copyright 2005-2007, 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.widgets; >- >-import java.util.List; >- >-import org.eclipse.zest.layouts.LayoutAlgorithm; >- >-/** >- * This interface describes all Zest components that are Containers. This is an internal interface >- * and thus should not be used outside of Zest. Implementors of this interface must include the >- * following two methods >- * o addNode(GraphNode) >- * o addNode(GraphContainer) >- * >- * These are not actually listed here because Java does not allow protected methods in >- * interfaces. >- * >- * @author Ian Bull >- */ >-public interface IContainer { >- >- public Graph getGraph(); >- >- // All implementers must include this method >- /* protected void addNode(GraphNode node); */ >- >- // All implementers must include this method >- /* protected void addNode(GraphContainer container); */ >- >- public int getItemType(); >- >- /** >- * Re-applies the current layout algorithm >- */ >- public void applyLayout(); >- >- /** >- * Sets the LayoutAlgorithm for this container and optionally applies it. >- * >- * @param algorithm The layout algorithm to set >- * @param applyLayout >- */ >- public void setLayoutAlgorithm(LayoutAlgorithm algorithm, boolean applyLayout); >- >- public List getNodes(); >- >- /* protected void highlightNode(GraphNode node); */ >- >- /* protected void highlightNode(GraphContainer container);*/ >- >- /* protected void unhighlightNode(GraphNode node); */ >- >- /* protected void unhighlightNode(GraphContainer container);*/ >- >-} // end of IContainer >+/******************************************************************************* >+ * Copyright 2005-2010, 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 >+ * Mateusz Matela >+ ******************************************************************************/ >+package org.eclipse.zest.core.widgets; >+ >+import java.util.List; >+ >+import org.eclipse.draw2d.IFigure; >+import org.eclipse.swt.widgets.Widget; >+import org.eclipse.zest.layouts.LayoutAlgorithm; >+import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle; >+ >+/** >+ * @noimplement This interface is not intended to be implemented by clients. >+ * >+ */ >+public interface IContainer { >+ >+ public abstract Graph getGraph(); >+ >+ /** >+ * @since 2.0 >+ */ >+ public Widget getItem(); >+ >+ public abstract List getNodes(); >+ >+ /** >+ * Returns list of connections laying inside this container. Only >+ * connections which both source and target nodes lay directly in this >+ * container are returned. >+ * >+ * @return >+ * @since 2.0 >+ */ >+ public abstract List getConnections(); >+ >+ /** >+ * @noreference This method is not intended to be referenced by clients. >+ * @param graphNode >+ */ >+ public abstract void addNode(GraphNode graphNode); >+ >+ /** >+ * @noreference This method is not intended to be referenced by clients. >+ * @param figure >+ */ >+ public abstract void addSubgraphFigure(IFigure figure); >+ >+ public abstract int getItemType(); >+ >+ /** >+ * @return >+ * @since 2.0 >+ */ >+ public abstract DisplayIndependentRectangle getLayoutBounds(); >+ >+ /** >+ * @noreference This method is not intended to be referenced by clients. >+ * @return >+ */ >+ public abstract InternalLayoutContext getLayoutContext(); >+ >+ public void applyLayout(); >+ >+ public void setLayoutAlgorithm(LayoutAlgorithm algorithm, boolean apply); >+ >+ /** >+ * Takes a list of connections and returns only those which source and >+ * target nodes lay directly in this container. >+ * >+ * @param connections >+ * list of GraphConnection to filter >+ * @return filtered list >+ */ >+ // protected List filterConnections(List connections) { >+ // List result = new ArrayList(); >+ // for (Iterator iterator = connections.iterator(); iterator.hasNext();) { >+ // GraphConnection connection = (GraphConnection) iterator.next(); >+ // if (connection.getSource().getParent() == this && >+ // connection.getDestination().getParent() == this) >+ // result.add(connection); >+ // } >+ // return result; >+ // } >+} >Index: src/org/eclipse/zest/core/widgets/InternalLayoutContext.java >=================================================================== >RCS file: src/org/eclipse/zest/core/widgets/InternalLayoutContext.java >diff -N src/org/eclipse/zest/core/widgets/InternalLayoutContext.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/zest/core/widgets/InternalLayoutContext.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,540 @@ >+/******************************************************************************* >+ * Copyright (c) 2009-2010 Mateusz Matela and others. 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: Mateusz Matela - initial API and implementation >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.core.widgets; >+ >+import java.util.ArrayList; >+import java.util.Arrays; >+import java.util.HashSet; >+import java.util.Iterator; >+import java.util.List; >+ >+import org.eclipse.draw2d.Animation; >+import org.eclipse.zest.layouts.LayoutAlgorithm; >+import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle; >+import org.eclipse.zest.layouts.interfaces.ConnectionLayout; >+import org.eclipse.zest.layouts.interfaces.ContextListener; >+import org.eclipse.zest.layouts.interfaces.EntityLayout; >+import org.eclipse.zest.layouts.interfaces.ExpandCollapseManager; >+import org.eclipse.zest.layouts.interfaces.GraphStructureListener; >+import org.eclipse.zest.layouts.interfaces.LayoutContext; >+import org.eclipse.zest.layouts.interfaces.LayoutListener; >+import org.eclipse.zest.layouts.interfaces.NodeLayout; >+import org.eclipse.zest.layouts.interfaces.PruningListener; >+import org.eclipse.zest.layouts.interfaces.SubgraphLayout; >+ >+class InternalLayoutContext implements LayoutContext { >+ >+ final IContainer container; >+ private final List filters = new ArrayList(); >+ private final List contextListeners = new ArrayList(); >+ private final List graphStructureListeners = new ArrayList(); >+ private final List layoutListeners = new ArrayList(); >+ private final List pruningListeners = new ArrayList(); >+ private LayoutAlgorithm mainAlgorithm; >+ private LayoutAlgorithm layoutAlgorithm; >+ private ExpandCollapseManager expandCollapseManager; >+ private SubgraphFactory subgraphFactory = new DefaultSubgraph.DefaultSubgraphFactory(); >+ private final HashSet subgraphs = new HashSet(); >+ private boolean eventsOn = true; >+ private boolean backgorundLayoutEnabled = true; >+ private boolean externalLayoutInvocation = false; >+ >+ /** >+ * @param graph >+ * the graph owning this context >+ */ >+ InternalLayoutContext(Graph graph) { >+ this.container = graph; >+ } >+ >+ InternalLayoutContext(GraphContainer container) { >+ this.container = container; >+ } >+ >+ public void addContextListener(ContextListener listener) { >+ contextListeners.add(listener); >+ } >+ >+ public void addGraphStructureListener(GraphStructureListener listener) { >+ graphStructureListeners.add(listener); >+ } >+ >+ public void addLayoutListener(LayoutListener listener) { >+ layoutListeners.add(listener); >+ } >+ >+ public void addPruningListener(PruningListener listener) { >+ pruningListeners.add(listener); >+ } >+ >+ public SubgraphLayout createSubgraph(NodeLayout[] nodes) { >+ checkChangesAllowed(); >+ InternalNodeLayout[] internalNodes = new InternalNodeLayout[nodes.length]; >+ for (int i = 0; i < nodes.length; i++) { >+ internalNodes[i] = (InternalNodeLayout) nodes[i]; >+ } >+ SubgraphLayout subgraph = subgraphFactory.createSubgraph(internalNodes, >+ this); >+ subgraphs.add(subgraph); >+ return subgraph; >+ } >+ >+ void removeSubgrah(DefaultSubgraph subgraph) { >+ subgraphs.remove(subgraph); >+ } >+ >+ public void flushChanges(boolean animationHint) { >+ // TODO support for asynchronous call >+ if (!container.getGraph().isVisible() && animationHint) { >+ return; >+ } >+ eventsOn = false; >+ if (animationHint) { >+ Animation.markBegin(); >+ } >+ for (Iterator iterator = container.getNodes().iterator(); iterator >+ .hasNext();) { >+ GraphNode node = (GraphNode) iterator.next(); >+ node.applyLayoutChanges(); >+ } >+ for (Iterator iterator = container.getConnections().iterator(); iterator >+ .hasNext();) { >+ GraphConnection connection = (GraphConnection) iterator.next(); >+ connection.applyLayoutChanges(); >+ } >+ for (Iterator iterator = subgraphs.iterator(); iterator.hasNext();) { >+ DefaultSubgraph subgraph = (DefaultSubgraph) iterator.next(); >+ subgraph.applyLayoutChanges(); >+ } >+ if (animationHint) { >+ Animation.run(Graph.ANIMATION_TIME); >+ } >+ eventsOn = true; >+ } >+ >+ public DisplayIndependentRectangle getBounds() { >+ DisplayIndependentRectangle result = new DisplayIndependentRectangle( >+ container.getLayoutBounds()); >+ result.width -= 20; >+ result.height -= 20; >+ return result; >+ } >+ >+ public LayoutAlgorithm getMainLayoutAlgorithm() { >+ return mainAlgorithm; >+ } >+ >+ public ExpandCollapseManager getExpandCollapseManager() { >+ return expandCollapseManager; >+ } >+ >+ public NodeLayout[] getNodes() { >+ ArrayList result = new ArrayList(); >+ for (Iterator iterator = this.container.getNodes().iterator(); iterator >+ .hasNext();) { >+ GraphNode node = (GraphNode) iterator.next(); >+ if (!isLayoutItemFiltered(node)) { >+ result.add(node.getLayout()); >+ } >+ } >+ return (NodeLayout[]) result.toArray(new NodeLayout[result.size()]); >+ } >+ >+ public EntityLayout[] getEntities() { >+ HashSet addedSubgraphs = new HashSet(); >+ ArrayList result = new ArrayList(); >+ for (Iterator iterator = this.container.getNodes().iterator(); iterator >+ .hasNext();) { >+ GraphNode node = (GraphNode) iterator.next(); >+ if (!isLayoutItemFiltered(node)) { >+ InternalNodeLayout nodeLayout = node.getLayout(); >+ if (!nodeLayout.isPruned()) { >+ result.add(nodeLayout); >+ } else { >+ SubgraphLayout subgraph = nodeLayout.getSubgraph(); >+ if (subgraph.isGraphEntity() >+ && !addedSubgraphs.contains(subgraph)) { >+ result.add(subgraph); >+ addedSubgraphs.add(subgraph); >+ } >+ } >+ } >+ } >+ return (EntityLayout[]) result.toArray(new EntityLayout[result.size()]); >+ } >+ >+ public SubgraphLayout[] getSubgraphs() { >+ SubgraphLayout[] result = new SubgraphLayout[subgraphs.size()]; >+ int subgraphCount = 0; >+ for (Iterator iterator = subgraphs.iterator(); iterator.hasNext();) { >+ SubgraphLayout subgraph = (SubgraphLayout) iterator.next(); >+ NodeLayout[] nodes = subgraph.getNodes(); >+ for (int i = 0; i < nodes.length; i++) { >+ if (!isLayoutItemFiltered(((InternalNodeLayout) nodes[i]) >+ .getNode())) { >+ result[subgraphCount++] = subgraph; >+ break; >+ } >+ } >+ } >+ if (subgraphCount == subgraphs.size()) { >+ return result; >+ } else { >+ SubgraphLayout[] result2 = new SubgraphLayout[subgraphCount]; >+ System.arraycopy(result, 0, result2, 0, subgraphCount); >+ return result2; >+ } >+ } >+ >+ public boolean isBoundsExpandable() { >+ return false; >+ } >+ >+ public boolean isBackgroundLayoutEnabled() { >+ return backgorundLayoutEnabled; >+ } >+ >+ void setBackgroundLayoutEnabled(boolean enabled) { >+ if (this.backgorundLayoutEnabled != enabled) { >+ this.backgorundLayoutEnabled = enabled; >+ fireBackgroundEnableChangedEvent(); >+ } >+ } >+ >+ public boolean isPruningEnabled() { >+ return expandCollapseManager != null; >+ } >+ >+ public void removeContextListener(ContextListener listener) { >+ contextListeners.remove(listener); >+ } >+ >+ public void removeGraphStructureListener(GraphStructureListener listener) { >+ graphStructureListeners.remove(listener); >+ } >+ >+ public void removeLayoutListener(LayoutListener listener) { >+ layoutListeners.remove(listener); >+ } >+ >+ public void removePruningListener(PruningListener listener) { >+ pruningListeners.remove(listener); >+ } >+ >+ public void setMainLayoutAlgorithm(LayoutAlgorithm algorithm) { >+ mainAlgorithm = algorithm; >+ } >+ >+ public void setExpandCollapseManager( >+ ExpandCollapseManager expandCollapseManager) { >+ this.expandCollapseManager = expandCollapseManager; >+ expandCollapseManager.initExpansion(this); >+ } >+ >+ public ConnectionLayout[] getConnections() { >+ List connections = container.getConnections(); >+ ConnectionLayout[] result = new ConnectionLayout[connections.size()]; >+ int i = 0; >+ for (Iterator iterator = connections.iterator(); iterator.hasNext();) { >+ GraphConnection connection = (GraphConnection) iterator.next(); >+ if (!isLayoutItemFiltered(connection)) { >+ result[i++] = connection.getLayout(); >+ } >+ } >+ if (i == result.length) { >+ return result; >+ } >+ ConnectionLayout[] result2 = new ConnectionLayout[i]; >+ System.arraycopy(result, 0, result2, 0, i); >+ return result2; >+ } >+ >+ public ConnectionLayout[] getConnections(EntityLayout source, >+ EntityLayout target) { >+ ArrayList result = new ArrayList(); >+ >+ ArrayList sourcesList = new ArrayList(); >+ if (source instanceof NodeLayout) { >+ sourcesList.add(source); >+ } >+ if (source instanceof SubgraphLayout) { >+ sourcesList.addAll(Arrays.asList(((SubgraphLayout) source) >+ .getNodes())); >+ } >+ >+ HashSet targets = new HashSet(); >+ if (target instanceof NodeLayout) { >+ targets.add(target); >+ } >+ if (target instanceof SubgraphLayout) { >+ targets.addAll(Arrays.asList(((SubgraphLayout) target).getNodes())); >+ } >+ >+ for (Iterator iterator = sourcesList.iterator(); iterator.hasNext();) { >+ NodeLayout source2 = (NodeLayout) iterator.next(); >+ ConnectionLayout[] outgoingConnections = source2 >+ .getOutgoingConnections(); >+ for (int i = 0; i < outgoingConnections.length; i++) { >+ ConnectionLayout connection = outgoingConnections[i]; >+ if ((connection.getSource() == source2 && targets >+ .contains(connection.getTarget())) >+ || (connection.getTarget() == source2 && targets >+ .contains(connection.getSource()))) { >+ result.add(connection); >+ } >+ } >+ >+ } >+ return (ConnectionLayout[]) result.toArray(new ConnectionLayout[result >+ .size()]); >+ } >+ >+ void addFilter(LayoutFilter filter) { >+ filters.add(filter); >+ } >+ >+ void removeFilter(LayoutFilter filter) { >+ filters.remove(filter); >+ } >+ >+ boolean isLayoutItemFiltered(GraphItem item) { >+ for (Iterator it = filters.iterator(); it.hasNext();) { >+ LayoutFilter filter = (LayoutFilter) it.next(); >+ if (filter.isObjectFiltered(item)) { >+ return true; >+ } >+ } >+ return false; >+ } >+ >+ void setExpanded(NodeLayout node, boolean expanded) { >+ externalLayoutInvocation = true; >+ if (expandCollapseManager != null) { >+ expandCollapseManager.setExpanded(this, node, expanded); >+ } >+ externalLayoutInvocation = false; >+ } >+ >+ boolean canExpand(NodeLayout node) { >+ return expandCollapseManager != null >+ && expandCollapseManager.canExpand(this, node); >+ } >+ >+ boolean canCollapse(NodeLayout node) { >+ return expandCollapseManager != null >+ && expandCollapseManager.canCollapse(this, node); >+ } >+ >+ void setSubgraphFactory(SubgraphFactory factory) { >+ subgraphFactory = factory; >+ } >+ >+ SubgraphFactory getSubgraphFactory() { >+ return subgraphFactory; >+ } >+ >+ void applyMainAlgorithm() { >+ if (backgorundLayoutEnabled && mainAlgorithm != null) { >+ mainAlgorithm.applyLayout(true); >+ flushChanges(false); >+ } >+ } >+ >+ /** >+ * Sets layout algorithm for this context. It differs from >+ * {@link #setMainLayoutAlgorithm(LayoutAlgorithm) main algorithm} in that >+ * it's always used when {@link #applyLayoutAlgorithm(boolean)} and not >+ * after firing of events. >+ */ >+ void setLayoutAlgorithm(LayoutAlgorithm algorithm) { >+ if (this.layoutAlgorithm != null) { >+ this.layoutAlgorithm.setLayoutContext(null); >+ } >+ this.layoutAlgorithm = algorithm; >+ this.layoutAlgorithm.setLayoutContext(this); >+ } >+ >+ LayoutAlgorithm getLayoutAlgorithm() { >+ return layoutAlgorithm; >+ } >+ >+ void applyLayout(boolean clean) { >+ if (layoutAlgorithm != null) { >+ externalLayoutInvocation = true; >+ layoutAlgorithm.applyLayout(clean); >+ externalLayoutInvocation = false; >+ } >+ } >+ >+ void checkChangesAllowed() { >+ if (!backgorundLayoutEnabled && !externalLayoutInvocation) { >+ throw new RuntimeException( >+ "Layout not allowed to perform changes in layout context!"); >+ } >+ } >+ >+ void fireNodeAddedEvent(NodeLayout node) { >+ boolean intercepted = !eventsOn; >+ GraphStructureListener[] listeners = (GraphStructureListener[]) graphStructureListeners >+ .toArray(new GraphStructureListener[graphStructureListeners >+ .size()]); >+ for (int i = 0; i < listeners.length && !intercepted; i++) { >+ intercepted = listeners[i].nodeAdded(this, node); >+ } >+ if (!intercepted) { >+ applyMainAlgorithm(); >+ } >+ } >+ >+ void fireNodeRemovedEvent(NodeLayout node) { >+ boolean intercepted = !eventsOn; >+ GraphStructureListener[] listeners = (GraphStructureListener[]) graphStructureListeners >+ .toArray(new GraphStructureListener[graphStructureListeners >+ .size()]); >+ for (int i = 0; i < listeners.length && !intercepted; i++) { >+ intercepted = listeners[i].nodeRemoved(this, node); >+ } >+ if (!intercepted) { >+ applyMainAlgorithm(); >+ } >+ } >+ >+ void fireConnectionAddedEvent(ConnectionLayout connection) { >+ InternalLayoutContext sourceContext = ((InternalNodeLayout) connection >+ .getSource()).getOwnerLayoutContext(); >+ InternalLayoutContext targetContext = ((InternalNodeLayout) connection >+ .getTarget()).getOwnerLayoutContext(); >+ if (sourceContext != targetContext) { >+ return; >+ } >+ if (sourceContext == this) { >+ boolean intercepted = !eventsOn; >+ GraphStructureListener[] listeners = (GraphStructureListener[]) graphStructureListeners >+ .toArray(new GraphStructureListener[graphStructureListeners >+ .size()]); >+ for (int i = 0; i < listeners.length && !intercepted; i++) { >+ intercepted = listeners[i].connectionAdded(this, connection); >+ } >+ if (!intercepted) { >+ applyMainAlgorithm(); >+ } >+ } else { >+ sourceContext.fireConnectionAddedEvent(connection); >+ } >+ } >+ >+ void fireConnectionRemovedEvent(ConnectionLayout connection) { >+ InternalLayoutContext sourceContext = ((InternalNodeLayout) connection >+ .getSource()).getOwnerLayoutContext(); >+ InternalLayoutContext targetContext = ((InternalNodeLayout) connection >+ .getTarget()).getOwnerLayoutContext(); >+ if (sourceContext != targetContext) { >+ return; >+ } >+ if (sourceContext == this) { >+ boolean intercepted = !eventsOn; >+ GraphStructureListener[] listeners = (GraphStructureListener[]) graphStructureListeners >+ .toArray(new GraphStructureListener[graphStructureListeners >+ .size()]); >+ for (int i = 0; i < listeners.length && !intercepted; i++) { >+ intercepted = listeners[i].connectionRemoved(this, connection); >+ } >+ if (!intercepted) { >+ applyMainAlgorithm(); >+ } >+ } else { >+ sourceContext.fireConnectionAddedEvent(connection); >+ } >+ } >+ >+ void fireBoundsChangedEvent() { >+ boolean intercepted = !eventsOn; >+ ContextListener[] listeners = (ContextListener[]) contextListeners >+ .toArray(new ContextListener[contextListeners.size()]); >+ for (int i = 0; i < listeners.length && !intercepted; i++) { >+ intercepted = listeners[i].boundsChanged(this); >+ } >+ if (!intercepted) { >+ applyMainAlgorithm(); >+ } >+ } >+ >+ void fireBackgroundEnableChangedEvent() { >+ ContextListener[] listeners = (ContextListener[]) contextListeners >+ .toArray(new ContextListener[contextListeners.size()]); >+ for (int i = 0; i < listeners.length; i++) { >+ listeners[i].backgroundEnableChanged(this); >+ } >+ } >+ >+ void fireNodeMovedEvent(InternalNodeLayout node) { >+ if (eventsOn) { >+ node.refreshLocation(); >+ } >+ boolean intercepted = !eventsOn; >+ LayoutListener[] listeners = (LayoutListener[]) layoutListeners >+ .toArray(new LayoutListener[layoutListeners.size()]); >+ node.setLocation(node.getNode().getLocation().x, node.getNode() >+ .getLocation().y); >+ for (int i = 0; i < listeners.length && !intercepted; i++) { >+ intercepted = listeners[i].nodeMoved(this, node); >+ } >+ if (!intercepted) { >+ applyMainAlgorithm(); >+ } >+ } >+ >+ void fireNodeResizedEvent(InternalNodeLayout node) { >+ if (eventsOn) { >+ node.refreshSize(); >+ node.refreshLocation(); >+ } >+ boolean intercepted = !eventsOn; >+ LayoutListener[] listeners = (LayoutListener[]) layoutListeners >+ .toArray(new LayoutListener[layoutListeners.size()]); >+ for (int i = 0; i < listeners.length && !intercepted; i++) { >+ intercepted = listeners[i].nodeResized(this, node); >+ } >+ if (!intercepted) { >+ applyMainAlgorithm(); >+ } >+ } >+ >+ void fireSubgraphMovedEvent(DefaultSubgraph subgraph) { >+ if (eventsOn) { >+ subgraph.refreshLocation(); >+ } >+ boolean intercepted = !eventsOn; >+ LayoutListener[] listeners = (LayoutListener[]) layoutListeners >+ .toArray(new LayoutListener[layoutListeners.size()]); >+ for (int i = 0; i < listeners.length && !intercepted; i++) { >+ intercepted = listeners[i].subgraphMoved(this, subgraph); >+ } >+ if (!intercepted) { >+ applyMainAlgorithm(); >+ } >+ } >+ >+ void fireSubgraphResizedEvent(DefaultSubgraph subgraph) { >+ if (eventsOn) { >+ subgraph.refreshSize(); >+ subgraph.refreshLocation(); >+ } >+ boolean intercepted = !eventsOn; >+ LayoutListener[] listeners = (LayoutListener[]) layoutListeners >+ .toArray(new LayoutListener[layoutListeners.size()]); >+ for (int i = 0; i < listeners.length && !intercepted; i++) { >+ intercepted = listeners[i].subgraphResized(this, subgraph); >+ } >+ if (!intercepted) { >+ applyMainAlgorithm(); >+ } >+ } >+} >\ No newline at end of file >Index: src/org/eclipse/zest/core/widgets/InternalNodeLayout.java >=================================================================== >RCS file: src/org/eclipse/zest/core/widgets/InternalNodeLayout.java >diff -N src/org/eclipse/zest/core/widgets/InternalNodeLayout.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/zest/core/widgets/InternalNodeLayout.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,329 @@ >+/******************************************************************************* >+ * Copyright (c) 2009-2010 Mateusz Matela and others. 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: Mateusz Matela - initial API and implementation >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.core.widgets; >+ >+import java.util.ArrayList; >+import java.util.HashMap; >+import java.util.HashSet; >+import java.util.Iterator; >+ >+import org.eclipse.draw2d.FigureListener; >+import org.eclipse.draw2d.IFigure; >+import org.eclipse.draw2d.geometry.Dimension; >+import org.eclipse.draw2d.geometry.Point; >+import org.eclipse.zest.layouts.dataStructures.DisplayIndependentDimension; >+import org.eclipse.zest.layouts.dataStructures.DisplayIndependentPoint; >+import org.eclipse.zest.layouts.interfaces.ConnectionLayout; >+import org.eclipse.zest.layouts.interfaces.EntityLayout; >+import org.eclipse.zest.layouts.interfaces.NodeLayout; >+import org.eclipse.zest.layouts.interfaces.SubgraphLayout; >+ >+class InternalNodeLayout implements NodeLayout { >+ >+ /** >+ * This listener is added to nodes' figures as a workaround for the problem >+ * of minimized nodes leaving single on the graph pixels when zoomed out >+ */ >+ private final static FigureListener figureListener = new FigureListener() { >+ public void figureMoved(IFigure source) { >+ // hide figures of minimized nodes >+ GraphNode node = (GraphNode) figureToNode.get(source); >+ if (node.getLayout().isMinimized() && source.getSize().equals(0, 0)) { >+ source.setVisible(false); >+ } else { >+ source.setVisible(node.isVisible()); >+ } >+ } >+ }; >+ private final static HashMap figureToNode = new HashMap(); >+ >+ private DisplayIndependentPoint location; >+ private DisplayIndependentDimension size; >+ private boolean minimized = false; >+ private final GraphNode node; >+ private final InternalLayoutContext ownerLayoutContext; >+ private DefaultSubgraph subgraph; >+ private boolean isDisposed = false; >+ >+ public InternalNodeLayout(GraphNode graphNode) { >+ this.node = graphNode; >+ this.ownerLayoutContext = node.parent.getLayoutContext(); >+ graphNode.nodeFigure.addFigureListener(figureListener); >+ figureToNode.put(graphNode.nodeFigure, graphNode); >+ } >+ >+ public DisplayIndependentPoint getLocation() { >+ if (location == null) { >+ refreshLocation(); >+ } >+ return new DisplayIndependentPoint(location); >+ } >+ >+ public DisplayIndependentDimension getSize() { >+ if (size == null) { >+ refreshSize(); >+ } >+ return new DisplayIndependentDimension(size); >+ } >+ >+ public SubgraphLayout getSubgraph() { >+ return subgraph; >+ } >+ >+ public boolean isMovable() { >+ return true; >+ } >+ >+ public boolean isPrunable() { >+ return ownerLayoutContext.isPruningEnabled(); >+ } >+ >+ public boolean isPruned() { >+ return subgraph != null; >+ } >+ >+ public boolean isResizable() { >+ return (node.parent.getItem().getStyle() & ZestStyles.NODES_NO_LAYOUT_RESIZE) == 0; >+ } >+ >+ public void prune(SubgraphLayout subgraph) { >+ if (subgraph != null && !(subgraph instanceof DefaultSubgraph)) { >+ throw new RuntimeException( >+ "InternalNodeLayout can be pruned only to instance of DefaultSubgraph."); >+ } >+ ownerLayoutContext.checkChangesAllowed(); >+ if (subgraph == this.subgraph) { >+ return; >+ } >+ if (this.subgraph != null) { >+ SubgraphLayout subgraph2 = this.subgraph; >+ this.subgraph = null; >+ subgraph2.removeNodes(new NodeLayout[] { this }); >+ } >+ if (subgraph != null) { >+ this.subgraph = (DefaultSubgraph) subgraph; >+ subgraph.addNodes(new NodeLayout[] { this }); >+ } >+ } >+ >+ public void setLocation(double x, double y) { >+ if (!ownerLayoutContext.isLayoutItemFiltered(this.getNode())) { >+ ownerLayoutContext.checkChangesAllowed(); >+ internalSetLocation(x, y); >+ } >+ } >+ >+ private void internalSetLocation(double x, double y) { >+ if (location != null) { >+ location.x = x; >+ location.y = y; >+ } else { >+ location = new DisplayIndependentPoint(x, y); >+ } >+ } >+ >+ public void setSize(double width, double height) { >+ ownerLayoutContext.checkChangesAllowed(); >+ internalSetSize(width, height); >+ } >+ >+ private void internalSetSize(double width, double height) { >+ if (size != null) { >+ size.width = width; >+ size.height = height; >+ } else { >+ size = new DisplayIndependentDimension(width, height); >+ } >+ } >+ >+ public void setMinimized(boolean minimized) { >+ ownerLayoutContext.checkChangesAllowed(); >+ getSize(); >+ this.minimized = minimized; >+ } >+ >+ public boolean isMinimized() { >+ return minimized; >+ } >+ >+ public NodeLayout[] getPredecessingNodes() { >+ ConnectionLayout[] connections = getIncomingConnections(); >+ NodeLayout[] result = new NodeLayout[connections.length]; >+ for (int i = 0; i < connections.length; i++) { >+ result[i] = connections[i].getSource(); >+ if (result[i] == this) { >+ result[i] = connections[i].getTarget(); >+ } >+ } >+ return result; >+ } >+ >+ public NodeLayout[] getSuccessingNodes() { >+ ConnectionLayout[] connections = getOutgoingConnections(); >+ NodeLayout[] result = new NodeLayout[connections.length]; >+ for (int i = 0; i < connections.length; i++) { >+ result[i] = connections[i].getTarget(); >+ if (result[i] == this) { >+ result[i] = connections[i].getSource(); >+ } >+ } >+ return result; >+ } >+ >+ public EntityLayout[] getSuccessingEntities() { >+ if (isPruned()) { >+ return new NodeLayout[0]; >+ } >+ ArrayList result = new ArrayList(); >+ HashSet addedSubgraphs = new HashSet(); >+ NodeLayout[] successingNodes = getSuccessingNodes(); >+ for (int i = 0; i < successingNodes.length; i++) { >+ if (!successingNodes[i].isPruned()) { >+ result.add(successingNodes[i]); >+ } else { >+ SubgraphLayout successingSubgraph = successingNodes[i] >+ .getSubgraph(); >+ if (successingSubgraph.isGraphEntity() >+ && !addedSubgraphs.contains(successingSubgraph)) { >+ result.add(successingSubgraph); >+ addedSubgraphs.add(successingSubgraph); >+ } >+ } >+ } >+ return (EntityLayout[]) result.toArray(new EntityLayout[result.size()]); >+ } >+ >+ public EntityLayout[] getPredecessingEntities() { >+ if (isPruned()) { >+ return new NodeLayout[0]; >+ } >+ ArrayList result = new ArrayList(); >+ HashSet addedSubgraphs = new HashSet(); >+ NodeLayout[] predecessingNodes = getPredecessingNodes(); >+ for (int i = 0; i < predecessingNodes.length; i++) { >+ if (!predecessingNodes[i].isPruned()) { >+ result.add(predecessingNodes[i]); >+ } else { >+ SubgraphLayout predecessingSubgraph = predecessingNodes[i] >+ .getSubgraph(); >+ if (predecessingSubgraph.isGraphEntity() >+ && !addedSubgraphs.contains(predecessingSubgraph)) { >+ result.add(predecessingSubgraph); >+ addedSubgraphs.add(predecessingSubgraph); >+ } >+ } >+ } >+ return (EntityLayout[]) result.toArray(new EntityLayout[result.size()]); >+ } >+ >+ public ConnectionLayout[] getIncomingConnections() { >+ ArrayList result = new ArrayList(); >+ for (Iterator iterator = node.getTargetConnections().iterator(); iterator >+ .hasNext();) { >+ GraphConnection connection = (GraphConnection) iterator.next(); >+ if (!ownerLayoutContext.isLayoutItemFiltered(connection)) { >+ result.add(connection.getLayout()); >+ } >+ } >+ for (Iterator iterator = node.getSourceConnections().iterator(); iterator >+ .hasNext();) { >+ GraphConnection connection = (GraphConnection) iterator.next(); >+ if (!connection.isDirected() >+ && !ownerLayoutContext.isLayoutItemFiltered(connection)) { >+ result.add(connection.getLayout()); >+ } >+ } >+ return (ConnectionLayout[]) result.toArray(new ConnectionLayout[result >+ .size()]); >+ } >+ >+ public ConnectionLayout[] getOutgoingConnections() { >+ ArrayList result = new ArrayList(); >+ for (Iterator iterator = node.getSourceConnections().iterator(); iterator >+ .hasNext();) { >+ GraphConnection connection = (GraphConnection) iterator.next(); >+ if (!ownerLayoutContext.isLayoutItemFiltered(connection)) { >+ result.add(connection.getLayout()); >+ } >+ } >+ for (Iterator iterator = node.getTargetConnections().iterator(); iterator >+ .hasNext();) { >+ GraphConnection connection = (GraphConnection) iterator.next(); >+ if (!connection.isDirected() >+ && !ownerLayoutContext.isLayoutItemFiltered(connection)) { >+ result.add(connection.getLayout()); >+ } >+ } >+ return (ConnectionLayout[]) result.toArray(new ConnectionLayout[result >+ .size()]); >+ } >+ >+ public double getPreferredAspectRatio() { >+ return 0; >+ } >+ >+ GraphNode getNode() { >+ return node; >+ } >+ >+ void applyLayout() { >+ if (minimized) { >+ node.setSize(0, 0); >+ if (location != null) { >+ node.setLocation(location.x, location.y); >+ } >+ } else { >+ node.setSize(-1, -1); >+ if (location != null) { >+ node.setLocation(location.x - getSize().width / 2, location.y >+ - size.height / 2); >+ } >+ if (size != null) { >+ Dimension currentSize = node.getSize(); >+ if (size.width != currentSize.width >+ || size.height != currentSize.height) { >+ node.setSize(size.width, size.height); >+ } >+ } >+ } >+ } >+ >+ InternalLayoutContext getOwnerLayoutContext() { >+ return ownerLayoutContext; >+ } >+ >+ void refreshSize() { >+ Dimension size2 = node.getSize(); >+ internalSetSize(size2.width, size2.height); >+ } >+ >+ void refreshLocation() { >+ Point location2 = node.getLocation(); >+ internalSetLocation(location2.x + getSize().width / 2, location2.y >+ + size.height / 2); >+ } >+ >+ public String toString() { >+ return node.toString() + "(layout)"; >+ } >+ >+ void dispose() { >+ isDisposed = true; >+ if (subgraph != null) { >+ subgraph.removeDisposedNodes(); >+ } >+ ownerLayoutContext.fireNodeRemovedEvent(node.getLayout()); >+ figureToNode.remove(node.nodeFigure); >+ } >+ >+ boolean isDisposed() { >+ return isDisposed; >+ } >+} >\ No newline at end of file >Index: src/org/eclipse/zest/core/widgets/LayoutFilter.java >=================================================================== >RCS file: src/org/eclipse/zest/core/widgets/LayoutFilter.java >diff -N src/org/eclipse/zest/core/widgets/LayoutFilter.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/zest/core/widgets/LayoutFilter.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,20 @@ >+/******************************************************************************* >+ * Copyright 2005-2010, 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 >+ * Mateusz Matela >+ ******************************************************************************/ >+package org.eclipse.zest.core.widgets; >+ >+/** >+ * @since 2.0 >+ */ >+public interface LayoutFilter { >+ >+ public boolean isObjectFiltered(GraphItem item); >+ >+} >Index: src/org/eclipse/zest/core/widgets/PrunedSuccessorsSubgraph.java >=================================================================== >RCS file: src/org/eclipse/zest/core/widgets/PrunedSuccessorsSubgraph.java >diff -N src/org/eclipse/zest/core/widgets/PrunedSuccessorsSubgraph.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/zest/core/widgets/PrunedSuccessorsSubgraph.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,272 @@ >+/******************************************************************************* >+ * Copyright (c) 2009-2010 Mateusz Matela and others. 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: Mateusz Matela - initial API and implementation >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.core.widgets; >+ >+import java.util.Arrays; >+import java.util.HashMap; >+import java.util.HashSet; >+import java.util.Iterator; >+ >+import org.eclipse.draw2d.AncestorListener; >+import org.eclipse.draw2d.ColorConstants; >+import org.eclipse.draw2d.FigureListener; >+import org.eclipse.draw2d.IFigure; >+import org.eclipse.draw2d.geometry.Dimension; >+import org.eclipse.draw2d.geometry.Point; >+import org.eclipse.draw2d.geometry.Rectangle; >+import org.eclipse.swt.graphics.Font; >+import org.eclipse.swt.graphics.FontData; >+import org.eclipse.swt.widgets.Display; >+import org.eclipse.zest.core.widgets.internal.GraphLabel; >+import org.eclipse.zest.core.widgets.internal.ZestRootLayer; >+import org.eclipse.zest.layouts.interfaces.LayoutContext; >+import org.eclipse.zest.layouts.interfaces.NodeLayout; >+ >+/** >+ * A subgraph that for each unexpanded node in a graph adds a label showing >+ * number of pruned successors (as unexpanded node is considered a node for >+ * which {@link Graph#canExpand(GraphNode)} returns true AND >+ * {@link Graph#canCollapse(GraphNode)} returns false). It doesn't matter which >+ * subgraph a node is pruned to, so the factory for this subgraph uses one >+ * instance for whole layout context. >+ */ >+class PrunedSuccessorsSubgraph extends DefaultSubgraph { >+ >+ private class LabelAncestorListener extends AncestorListener.Stub { >+ private final IFigure originalFigure; >+ private IFigure fisheyeFigure; >+ >+ public LabelAncestorListener(IFigure originalFigure, >+ IFigure fisheyeFigure) { >+ this.originalFigure = originalFigure; >+ this.fisheyeFigure = fisheyeFigure; >+ } >+ >+ public void ancestorRemoved(IFigure ancestor) { >+ if (fisheyeFigure != null) { >+ final GraphLabel label = (GraphLabel) nodeFigureToLabel >+ .get(fisheyeFigure); >+ if (label == null) { >+ return; >+ } >+ nodeFigureToLabel.remove(fisheyeFigure); >+ Display.getDefault().asyncExec(new Runnable() { >+ public void run() { >+ label.removeAncestorListener(LabelAncestorListener.this); >+ } >+ }); >+ fisheyeFigure.removeFigureListener(nodeFigureListener); >+ originalFigure.addFigureListener(nodeFigureListener); >+ labelToAncestorListener.remove(label); >+ fisheyeFigure = null; >+ addLabelForFigure(originalFigure, label); >+ refreshLabelBounds(originalFigure, label); >+ } >+ } >+ } >+ >+ private final FigureListener nodeFigureListener = new FigureListener() { >+ public void figureMoved(IFigure source) { >+ GraphLabel label = (GraphLabel) nodeFigureToLabel.get(source); >+ if (label != null) { >+ refreshLabelBounds(source, label); >+ } >+ } >+ }; >+ >+ private final FisheyeListener fisheyeListener = new FisheyeListener() { >+ >+ public void fisheyeReplaced(Graph graph, IFigure oldFisheyeFigure, >+ IFigure newFisheyeFigure) { >+ oldFisheyeFigure.removeFigureListener(nodeFigureListener); >+ newFisheyeFigure.addFigureListener(nodeFigureListener); >+ GraphLabel label = (GraphLabel) nodeFigureToLabel >+ .remove(oldFisheyeFigure); >+ nodeFigureToLabel.put(newFisheyeFigure, label); >+ >+ LabelAncestorListener ancestorListener = (LabelAncestorListener) labelToAncestorListener >+ .get(label); >+ ancestorListener.fisheyeFigure = null; >+ addLabelForFigure(newFisheyeFigure, label); >+ ancestorListener.fisheyeFigure = newFisheyeFigure; >+ refreshLabelBounds(newFisheyeFigure, label); >+ } >+ >+ public void fisheyeRemoved(Graph graph, IFigure originalFigure, >+ IFigure fisheyeFigure) { >+ // do nothing - labelAncestorListener will take care of cleaning >+ // up >+ } >+ >+ public void fisheyeAdded(Graph graph, IFigure originalFigure, >+ IFigure fisheyeFigure) { >+ originalFigure.removeFigureListener(nodeFigureListener); >+ fisheyeFigure.addFigureListener(nodeFigureListener); >+ GraphLabel label = (GraphLabel) nodeFigureToLabel >+ .get(originalFigure); >+ if (label == null) { >+ return; >+ } >+ nodeFigureToLabel.put(fisheyeFigure, label); >+ refreshLabelBounds(fisheyeFigure, label); >+ addLabelForFigure(fisheyeFigure, label); >+ LabelAncestorListener labelAncestorListener = new LabelAncestorListener( >+ originalFigure, fisheyeFigure); >+ label.addAncestorListener(labelAncestorListener); >+ labelToAncestorListener.put(label, labelAncestorListener); >+ } >+ }; >+ >+ /** >+ * Maps from figures of nodes to labels showing number of nodes hidden >+ * successors >+ */ >+ private HashMap nodeFigureToLabel = new HashMap(); >+ >+ private HashMap labelToAncestorListener = new HashMap(); >+ >+ protected PrunedSuccessorsSubgraph(LayoutContext context2) { >+ super(context2); >+ context.container.getGraph().addFisheyeListener(fisheyeListener); >+ } >+ >+ public void addNodes(NodeLayout[] nodes) { >+ super.addNodes(nodes); >+ HashSet nodesToUpdate = new HashSet(); >+ for (int i = 0; i < nodes.length; i++) { >+ nodesToUpdate >+ .addAll(Arrays.asList(nodes[i].getPredecessingNodes())); >+ } >+ for (Iterator iterator = nodesToUpdate.iterator(); iterator.hasNext();) { >+ InternalNodeLayout nodeToUpdate = (InternalNodeLayout) iterator >+ .next(); >+ updateNodeLabel(nodeToUpdate); >+ } >+ >+ } >+ >+ public void removeNodes(NodeLayout[] nodes) { >+ super.removeNodes(nodes); >+ HashSet nodesToUpdate = new HashSet(); >+ for (int i = 0; i < nodes.length; i++) { >+ nodesToUpdate >+ .addAll(Arrays.asList(nodes[i].getPredecessingNodes())); >+ if (((InternalNodeLayout) nodes[i]).isDisposed()) { >+ removeFigureForNode((InternalNodeLayout) nodes[i]); >+ } else { >+ nodesToUpdate.add(nodes[i]); >+ } >+ } >+ for (Iterator iterator = nodesToUpdate.iterator(); iterator.hasNext();) { >+ InternalNodeLayout predecessor = (InternalNodeLayout) iterator >+ .next(); >+ updateNodeLabel(predecessor); >+ } >+ } >+ >+ private void addLabelForFigure(IFigure figure, GraphLabel label) { >+ IFigure parent = figure.getParent(); >+ if (parent instanceof ZestRootLayer) { >+ ((ZestRootLayer) parent).addDecoration(figure, label); >+ } else { >+ if (parent.getChildren().contains(label)) { >+ parent.remove(label); >+ } >+ int index = parent.getChildren().indexOf(figure); >+ parent.add(label, index + 1); >+ } >+ } >+ >+ private void refreshLabelBounds(IFigure figure, GraphLabel label) { >+ Rectangle figureBounds = figure.getBounds(); >+ if (figureBounds.width * figureBounds.height > 0) { >+ label.setText(label.getText()); // hack: resets label's size >+ Dimension labelSize = label.getSize(); >+ labelSize.expand(-6, -4); >+ Point anchorPoint = figure.getBounds().getBottomRight(); >+ anchorPoint.x -= labelSize.width / 2; >+ anchorPoint.y -= labelSize.height / 2; >+ Rectangle bounds = new Rectangle(anchorPoint, labelSize); >+ label.setBounds(bounds); >+ label.getParent().setConstraint(label, bounds); >+ } else { >+ label.getParent().setConstraint(label, >+ new Rectangle(figureBounds.x, figureBounds.y, 0, 0)); >+ label.setBounds(new Rectangle(figureBounds.x, figureBounds.y, 0, 0)); >+ } >+ } >+ >+ void updateNodeLabel(InternalNodeLayout internalNode) { >+ if (internalNode.isDisposed()) { >+ return; >+ } >+ IFigure figure = internalNode.getNode().getFigure(); >+ GraphLabel label = (GraphLabel) nodeFigureToLabel.get(figure); >+ IFigure fisheye = getFisheyeFigure(figure); >+ if (fisheye != null) { >+ figure = fisheye; >+ } >+ if (label == null) { >+ label = new GraphLabel(false); >+ label.setForegroundColor(ColorConstants.white); >+ label.setBackgroundColor(ColorConstants.red); >+ FontData fontData = Display.getDefault().getSystemFont() >+ .getFontData()[0]; >+ fontData.setHeight(6); >+ label.setFont(new Font(Display.getCurrent(), fontData)); >+ figure.addFigureListener(nodeFigureListener); >+ addLabelForFigure(figure, label); >+ nodeFigureToLabel.put(figure, label); >+ } >+ >+ GraphNode graphNode = internalNode.getNode(); >+ if (!graphNode.getGraphModel().canExpand(graphNode) >+ || graphNode.getGraphModel().canCollapse(graphNode) >+ || internalNode.isPruned()) { >+ label.setVisible(false); >+ } else { >+ NodeLayout[] successors = internalNode.getSuccessingNodes(); >+ int numberOfHiddenSuccessors = 0; >+ for (int i = 0; i < successors.length; i++) { >+ if (successors[i].isPruned()) { >+ numberOfHiddenSuccessors++; >+ } >+ } >+ String labelText = numberOfHiddenSuccessors > 0 ? "" >+ + numberOfHiddenSuccessors : ""; >+ if (!labelText.equals(label.getText())) { >+ label.setText(labelText); >+ } >+ label.setVisible(true); >+ } >+ >+ refreshLabelBounds(figure, label); >+ } >+ >+ private IFigure getFisheyeFigure(IFigure originalFigure) { >+ // a node has a fisheye if and only if its label has an AncestorListener >+ GraphLabel label = (GraphLabel) nodeFigureToLabel.get(originalFigure); >+ LabelAncestorListener ancestorListener = (LabelAncestorListener) labelToAncestorListener >+ .get(label); >+ if (ancestorListener != null) { >+ return ancestorListener.fisheyeFigure; >+ } >+ return null; >+ } >+ >+ private void removeFigureForNode(InternalNodeLayout internalNode) { >+ IFigure figure = internalNode.getNode().getFigure(); >+ GraphLabel label = (GraphLabel) nodeFigureToLabel.get(figure); >+ if (label != null && label.getParent() != null) { >+ label.getParent().remove(label); >+ } >+ nodeFigureToLabel.remove(figure); >+ } >+} >Index: src/org/eclipse/zest/core/widgets/SubgraphFactory.java >=================================================================== >RCS file: src/org/eclipse/zest/core/widgets/SubgraphFactory.java >diff -N src/org/eclipse/zest/core/widgets/SubgraphFactory.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/zest/core/widgets/SubgraphFactory.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,25 @@ >+/******************************************************************************* >+ * Copyright (c) 2009-2010 Mateusz Matela and others. 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: Mateusz Matela - initial API and implementation >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.core.widgets; >+ >+import org.eclipse.zest.layouts.interfaces.LayoutContext; >+import org.eclipse.zest.layouts.interfaces.NodeLayout; >+import org.eclipse.zest.layouts.interfaces.SubgraphLayout; >+ >+/** >+ * Factory used by {@link Graph} to create subgraphs. One instance of >+ * SubgraphFactory can be used with multiple graphs unless explicitly stated >+ * otherwise. >+ * >+ * @since 2.0 >+ */ >+public interface SubgraphFactory { >+ SubgraphLayout createSubgraph(NodeLayout[] nodes, LayoutContext context); >+} >Index: src/org/eclipse/zest/core/widgets/ZestStyles.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/widgets/ZestStyles.java,v >retrieving revision 1.7 >diff -u -r1.7 ZestStyles.java >--- src/org/eclipse/zest/core/widgets/ZestStyles.java 12 Sep 2007 20:44:39 -0000 1.7 >+++ src/org/eclipse/zest/core/widgets/ZestStyles.java 15 Mar 2010 03:49:00 -0000 >@@ -1,142 +1,137 @@ >-/******************************************************************************* >- * Copyright 2005-2007, 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.widgets; >- >-/** >- * Style constants used in Zest. >- * >- * @author Chris Callendar >- */ >-public final class ZestStyles { >- >- /** >- * A constant known to be zero (0), used in operations which take bit flags >- * to indicate that "no bits are set". >- */ >- public static final int NONE = 0; >- >- /** >- * Style constant indicating that invisible nodes should be ignored for >- * layouts. >- */ >- public static final int IGNORE_INVISIBLE_LAYOUT = 1 << 1; >- >- /** >- * Style constant indicating if the selected node's neighbors should be >- * highlighted. Note: this is a node-level style. It should not be applied >- * to graph views during construction. >- * >- */ >- //public static final int NODES_HIGHLIGHT_ADJACENT = 1 << 1; >- /** >- * Style constant indicating that node labels should be cached. This is >- * important under GTK+ because font drawing is slower than Windows. >- */ >- public static final int NODES_CACHE_LABEL = 1 << 1; >- >- /** >- * Style to specify that the node should contain a fisheye label >- * when the mouse moves over it. By default the fisheye node is just the >- * label with larger text. >- */ >- public static final int NODES_FISHEYE = 1 << 2; >- >- /** >- * Style to specify that the node should not show its text (only its image). >- * This with the NODES_FISHEYE style should help with large graphs (since the >- * fisheye style will show the text). >- */ >- public static final int NODES_HIDE_TEXT = 1 << 3; >- >- /** >- * Style constant indiciating that nodes should not be resized on layout. >- */ >- public static final int NODES_NO_LAYOUT_RESIZE = 1 << 4; >- /** >- * Style indicating that connections should show their direction by default. >- */ >- public static final int CONNECTIONS_DIRECTED = 1 << 1; >- >- /** >- * Style constant to indicate that connections should be drawn with solid >- * lines (this is the default). >- */ >- public static final int CONNECTIONS_SOLID = 1 << 2; >- /** >- * Style constant to indicate that connections should be drawn with dashed >- * lines. >- */ >- public static final int CONNECTIONS_DASH = 1 << 3; >- /** >- * Style constant to indicate that connections should be drawn with dotted >- * lines. >- */ >- public static final int CONNECTIONS_DOT = 1 << 4; >- /** >- * Style constant to indicate that connections should be drawn with >- * dash-dotted lines. >- */ >- public static final int CONNECTIONS_DASH_DOT = 1 << 5; >- >- /** >- * Bitwise ANDs the styleToCheck integer with the given style. >- * >- * @param style >- * @param styleToCheck >- * @return boolean if styleToCheck is part of the style >- */ >- public static boolean checkStyle(int style, int styleToCheck) { >- return ((style & styleToCheck) == styleToCheck); >- } >- >- /** >- * Validates the given style for connections to see if it is legal. Returns >- * false if not. >- * >- * @param style >- * the style to check. >- * @return true iff the given style is legal. >- */ >- public static boolean validateConnectionStyle(int styleToValidate) { >- int style = styleToValidate; >- // int illegal = CONNECTIONS_CURVED | CONNECTIONS_STRAIGHT | >- // CONNECTIONS_BEZIER; >- /* >- int illegal = CONNECTIONS_STRAIGHT; >- style &= illegal; >- int rightBit = style & (-style); >- boolean okay = (style == rightBit); >- if (!okay) { >- return okay; >- } >- */ >- >- int illegal = CONNECTIONS_DASH_DOT | CONNECTIONS_DASH | CONNECTIONS_DOT | CONNECTIONS_SOLID; >- style = styleToValidate; >- style &= illegal; >- int rightBit = style & (-style); >- boolean okay = (style == rightBit); >- if (!okay) { >- return okay; >- } >- >- return true; >- >- // @tag zest.bug.160368-ConnectionAlign.fix : must check the connections >- // to make sure that there isnt' an illegal combination of alignments. >- /* >- illegal = CONNECTIONS_VALIGN_BOTTOM | CONNECTIONS_VALIGN_MIDDLE | CONNECTIONS_VALIGN_TOP; >- style = styleToValidate; >- style &= illegal; >- rightBit = style & (-style); >- return (style == rightBit); >- */ >- } >-} >+/******************************************************************************* >+ * Copyright 2005-2010, 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.widgets; >+ >+/** >+ * Style constants used in Zest. >+ * >+ * @author Chris Callendar >+ */ >+public final class ZestStyles { >+ >+ /** >+ * A constant known to be zero (0), used in operations which take bit flags >+ * to indicate that "no bits are set". >+ */ >+ public static final int NONE = 0; >+ >+ /** >+ * Style constant indicating that invisible nodes should be ignored for >+ * layouts. >+ */ >+ public static final int IGNORE_INVISIBLE_LAYOUT = 1 << 1; >+ >+ /** >+ * Style constant indicating if the selected node's neighbors should be >+ * highlighted. Note: this is a node-level style. It should not be applied >+ * to graph views during construction. >+ * >+ */ >+ // public static final int NODES_HIGHLIGHT_ADJACENT = 1 << 1; >+ /** >+ * Style constant indicating that node labels should be cached. This is >+ * important under GTK+ because font drawing is slower than Windows. >+ */ >+ public static final int NODES_CACHE_LABEL = 1 << 1; >+ >+ /** >+ * Style to specify that the node should contain a fisheye label when the >+ * mouse moves over it. By default the fisheye node is just the label with >+ * larger text. >+ */ >+ public static final int NODES_FISHEYE = 1 << 2; >+ >+ /** >+ * Style to specify that the node should not show its text (only its image). >+ * This with the NODES_FISHEYE style should help with large graphs (since >+ * the fisheye style will show the text). >+ */ >+ public static final int NODES_HIDE_TEXT = 1 << 3; >+ >+ /** >+ * Style constant indiciating that nodes should not be resized on layout. >+ */ >+ public static final int NODES_NO_LAYOUT_RESIZE = 1 << 4; >+ /** >+ * Style indicating that connections should show their direction by default. >+ */ >+ public static final int CONNECTIONS_DIRECTED = 1 << 1; >+ >+ /** >+ * Style constant to indicate that connections should be drawn with solid >+ * lines (this is the default). >+ */ >+ public static final int CONNECTIONS_SOLID = 1 << 2; >+ /** >+ * Style constant to indicate that connections should be drawn with dashed >+ * lines. >+ */ >+ public static final int CONNECTIONS_DASH = 1 << 3; >+ /** >+ * Style constant to indicate that connections should be drawn with dotted >+ * lines. >+ */ >+ public static final int CONNECTIONS_DOT = 1 << 4; >+ /** >+ * Style constant to indicate that connections should be drawn with >+ * dash-dotted lines. >+ */ >+ public static final int CONNECTIONS_DASH_DOT = 1 << 5; >+ >+ /** >+ * Bitwise ANDs the styleToCheck integer with the given style. >+ * >+ * @param style >+ * @param styleToCheck >+ * @return boolean if styleToCheck is part of the style >+ */ >+ public static boolean checkStyle(int style, int styleToCheck) { >+ return ((style & styleToCheck) == styleToCheck); >+ } >+ >+ /** >+ * Validates the given style for connections to see if it is legal. Returns >+ * false if not. >+ * >+ * @param style >+ * the style to check. >+ * @return true iff the given style is legal. >+ */ >+ public static boolean validateConnectionStyle(int styleToValidate) { >+ int style = styleToValidate; >+ // int illegal = CONNECTIONS_CURVED | CONNECTIONS_STRAIGHT | >+ // CONNECTIONS_BEZIER; >+ /* >+ * int illegal = CONNECTIONS_STRAIGHT; style &= illegal; int rightBit = >+ * style & (-style); boolean okay = (style == rightBit); if (!okay) { >+ * return okay; } >+ */ >+ >+ int illegal = CONNECTIONS_DASH_DOT | CONNECTIONS_DASH | CONNECTIONS_DOT >+ | CONNECTIONS_SOLID; >+ style = styleToValidate; >+ style &= illegal; >+ int rightBit = style & (-style); >+ boolean okay = (style == rightBit); >+ if (!okay) { >+ return okay; >+ } >+ >+ return true; >+ >+ // @tag zest.bug.160368-ConnectionAlign.fix : must check the connections >+ // to make sure that there isnt' an illegal combination of alignments. >+ /* >+ * illegal = CONNECTIONS_VALIGN_BOTTOM | CONNECTIONS_VALIGN_MIDDLE | >+ * CONNECTIONS_VALIGN_TOP; style = styleToValidate; style &= illegal; >+ * rightBit = style & (-style); return (style == rightBit); >+ */ >+ } >+} >Index: src/org/eclipse/zest/core/widgets/custom/CGraphNode.java >=================================================================== >RCS file: src/org/eclipse/zest/core/widgets/custom/CGraphNode.java >diff -N src/org/eclipse/zest/core/widgets/custom/CGraphNode.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/zest/core/widgets/custom/CGraphNode.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,76 @@ >+/******************************************************************************* >+ * Copyright 2005-2010, 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 >+ * Mateusz Matela >+ ******************************************************************************/ >+package org.eclipse.zest.core.widgets.custom; >+ >+import org.eclipse.draw2d.IFigure; >+import org.eclipse.swt.graphics.Color; >+import org.eclipse.swt.graphics.Font; >+import org.eclipse.zest.core.widgets.Graph; >+import org.eclipse.zest.core.widgets.GraphContainer; >+import org.eclipse.zest.core.widgets.GraphNode; >+ >+/** >+ * A Custom Graph Node >+ * >+ * @since 2.0 >+ */ >+public class CGraphNode extends GraphNode { >+ >+ IFigure figure = null; >+ >+ /** >+ * @since 2.0 >+ */ >+ public CGraphNode(Graph graphModel, int style, IFigure figure) { >+ super(graphModel, style, figure); >+ } >+ >+ /** >+ * @since 2.0 >+ */ >+ public CGraphNode(GraphContainer graphModel, int style, IFigure figure) { >+ super(graphModel, style, figure); >+ } >+ >+ public IFigure getFigure() { >+ return super.getFigure(); >+ } >+ >+ protected IFigure createFigureForModel() { >+ this.figure = (IFigure) this.getData(); >+ return this.figure; >+ } >+ >+ public void setBackgroundColor(Color c) { >+ getFigure().setBackgroundColor(c); >+ } >+ >+ public void setFont(Font font) { >+ getFigure().setFont(font); >+ } >+ >+ public Color getBackgroundColor() { >+ return getFigure().getBackgroundColor(); >+ } >+ >+ public Font getFont() { >+ return getFigure().getFont(); >+ } >+ >+ public Color getForegroundColor() { >+ return getFigure().getForegroundColor(); >+ } >+ >+ protected void updateFigureForModel(IFigure currentFigure) { >+ // Undefined >+ } >+ >+} >Index: src/org/eclipse/zest/core/widgets/custom/LabelSubgraph.java >=================================================================== >RCS file: src/org/eclipse/zest/core/widgets/custom/LabelSubgraph.java >diff -N src/org/eclipse/zest/core/widgets/custom/LabelSubgraph.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/zest/core/widgets/custom/LabelSubgraph.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,68 @@ >+/******************************************************************************* >+ * Copyright (c) 2009-2010 Mateusz Matela and others. 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: Mateusz Matela - initial API and implementation >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.core.widgets.custom; >+ >+import org.eclipse.draw2d.Label; >+import org.eclipse.swt.graphics.Color; >+import org.eclipse.zest.core.widgets.FigureSubgraph; >+import org.eclipse.zest.core.widgets.internal.GraphLabel; >+import org.eclipse.zest.layouts.interfaces.LayoutContext; >+import org.eclipse.zest.layouts.interfaces.NodeLayout; >+ >+/** >+ * A subgraph layout that displays a label showing number of items pruned within >+ * it. >+ * >+ * @since 2.0 >+ */ >+public class LabelSubgraph extends FigureSubgraph { >+ >+ private Color backgroundColor; >+ private Color foregroundColor; >+ >+ /** >+ * Sets the foreground color of this subgraph (that is color of the text on >+ * the label). >+ * >+ * @param color >+ * color to set >+ */ >+ public void setForegroundColor(Color color) { >+ figure.setForegroundColor(color); >+ } >+ >+ /** >+ * Sets the background color of this subgraph's label. >+ * >+ * @param color >+ * color to set >+ */ >+ public void setBackgroundColor(Color color) { >+ figure.setBackgroundColor(color); >+ } >+ >+ protected void createFigure() { >+ figure = new GraphLabel(false); >+ figure.setForegroundColor(foregroundColor); >+ figure.setBackgroundColor(backgroundColor); >+ updateFigure(); >+ } >+ >+ protected void updateFigure() { >+ ((Label) figure).setText("" + nodes.size()); >+ } >+ >+ public LabelSubgraph(NodeLayout[] nodes, LayoutContext context, >+ Color foregroundColor, Color backgroundColor) { >+ super(nodes, context); >+ this.foregroundColor = foregroundColor; >+ this.backgroundColor = backgroundColor; >+ } >+} >\ No newline at end of file >Index: src/org/eclipse/zest/core/widgets/custom/TriangleSubgraph.java >=================================================================== >RCS file: src/org/eclipse/zest/core/widgets/custom/TriangleSubgraph.java >diff -N src/org/eclipse/zest/core/widgets/custom/TriangleSubgraph.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/zest/core/widgets/custom/TriangleSubgraph.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,255 @@ >+/******************************************************************************* >+ * Copyright (c) 2009-2010 Mateusz Matela and others. 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: Mateusz Matela - initial API and implementation >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.core.widgets.custom; >+ >+import java.util.HashMap; >+ >+import org.eclipse.draw2d.ColorConstants; >+import org.eclipse.draw2d.Graphics; >+import org.eclipse.draw2d.Shape; >+import org.eclipse.draw2d.geometry.PointList; >+import org.eclipse.draw2d.geometry.Rectangle; >+import org.eclipse.swt.graphics.Color; >+import org.eclipse.zest.core.widgets.FigureSubgraph; >+import org.eclipse.zest.layouts.algorithms.TreeLayoutObserver; >+import org.eclipse.zest.layouts.algorithms.TreeLayoutObserver.TreeListener; >+import org.eclipse.zest.layouts.algorithms.TreeLayoutObserver.TreeNode; >+import org.eclipse.zest.layouts.interfaces.LayoutContext; >+import org.eclipse.zest.layouts.interfaces.NodeLayout; >+import org.eclipse.zest.layouts.interfaces.SubgraphLayout; >+ >+/** >+ * A subgraph that is visualized in a graph as a triangle. It assumes that nodes >+ * in context that uses them are arranged in a tree structure and the nodes >+ * added to the subgraph are a subtree (except for the subtree's root, which >+ * should not be added). >+ * >+ * The triangle has three features that show the properties of a subtree >+ * contained within it: >+ * <ul> >+ * <li><b>Height of the triangle</b> is proportional to the height of the >+ * subtree. If the subtree contains the whole tree, the triangle's height will >+ * be equal to value provided with >+ * {@link TriangleSubgraph#setReferenceHeight(double)} (default is 50).</li> >+ * <li><b>Length of the triangle's base</b> depends on the number of leaves in >+ * the subtree. More precisely, it is proportional to the logarithm of the >+ * percent that the subtree's leaves make of the whole context's leaves. The >+ * proportion factor is adjusted so that for a subtree containing all the leaves >+ * the base has length provided with {@link TriangleSubgraph} >+ * {@link #setReferenceBase(double)} (default is 50) and for a subtree >+ * containing only one leaf the base has length 1.</li> >+ * <li><b>Background color of the triangle</b> depends on average number of >+ * children for nodes in the subtree. The less is this value, the more bright is >+ * the color (up to white for a subtree with average number of children equal to >+ * 1). The average value is calculated only for nodes that have at least one >+ * child. The root of the subtree (which is not directly added to this subgraph) >+ * is also accounted.</li> >+ * </ul> >+ * >+ * When the first subgraph of this class is created for a layout context, a >+ * {@link TreeLayoutObserver} is created for the context. It must keep track of >+ * changes in the graph structure, so events related to it should not be >+ * intercepted by other listeners before they reach the subgraph's observer. >+ * >+ * @since 2.0 >+ */ >+public class TriangleSubgraph extends FigureSubgraph { >+ >+ public static class TriangleParameters implements Cloneable { >+ public Color color = ColorConstants.black; >+ >+ public int direction = TOP_DOWN; >+ >+ public double referenceHeight = 50; >+ >+ public double referenceBase = 50; >+ >+ public Object clone() { >+ TriangleParameters result = new TriangleParameters(); >+ result.color = color; >+ result.direction = direction; >+ result.referenceHeight = referenceHeight; >+ result.referenceBase = referenceBase; >+ return result; >+ } >+ } >+ >+ private class IsoscelesTriangle extends Shape { >+ >+ private PointList points = new PointList(3); >+ >+ protected void fillShape(Graphics graphics) { >+ graphics.fillPolygon(points); >+ } >+ >+ protected void outlineShape(Graphics graphics) { >+ graphics.drawPolygon(points); >+ } >+ >+ protected void primTranslate(int dx, int dy) { >+ super.primTranslate(dx, dy); >+ points.translate(dx, dy); >+ } >+ >+ public void validate() { >+ super.validate(); >+ Rectangle r = new Rectangle(); >+ r.setBounds(getBounds()); >+ r.crop(getInsets()); >+ points.removeAllPoints(); >+ switch (parameters.direction) { >+ case TOP_DOWN: >+ points.addPoint(r.x + r.width / 2, r.y); >+ points.addPoint(r.x, r.y + r.height); >+ points.addPoint(r.x + r.width, r.y + r.height); >+ break; >+ case BOTTOM_UP: >+ points.addPoint(r.x + r.width / 2, r.y + r.height); >+ points.addPoint(r.x, r.y); >+ points.addPoint(r.x + r.width, r.y); >+ break; >+ case LEFT_RIGHT: >+ points.addPoint(r.x, r.y + r.height / 2); >+ points.addPoint(r.x + r.width, r.y); >+ points.addPoint(r.x + r.width, r.y + r.height); >+ break; >+ case RIGHT_LEFT: >+ points.addPoint(r.x + r.width, r.y + r.height / 2); >+ points.addPoint(r.x, r.y); >+ points.addPoint(r.x, r.y + r.height); >+ break; >+ } >+ } >+ } >+ >+ private static HashMap contextToTree = new HashMap(); >+ >+ private TriangleParameters parameters; >+ >+ public TriangleSubgraph(NodeLayout[] nodes, LayoutContext context, >+ TriangleParameters triangleParameters) { >+ super(nodes, context); >+ this.parameters = triangleParameters; >+ if (contextToTree.get(context) == null) { >+ TreeLayoutObserver treeLayoutObserver = new TreeLayoutObserver( >+ context, null); >+ treeLayoutObserver.addTreeListener(new TreeListener() { >+ protected void defaultHandle(TreeNode changedNode) { >+ SubgraphLayout subgraph = changedNode.getNode() >+ .getSubgraph(); >+ if (subgraph instanceof TriangleSubgraph) { >+ ((TriangleSubgraph) subgraph).updateFigure(); >+ } >+ } >+ }); >+ contextToTree.put(context, treeLayoutObserver); >+ } >+ } >+ >+ protected void createFigure() { >+ figure = new IsoscelesTriangle(); >+ figure.setBackgroundColor(parameters.color); >+ figure.setForegroundColor(parameters.color); >+ } >+ >+ private double log(double value, double base) { >+ return Math.log(value) / Math.log(base); >+ } >+ >+ protected void updateFigure() { >+ TreeLayoutObserver tree = (TreeLayoutObserver) contextToTree >+ .get(context); >+ TreeNode subgraphRoot = tree.getTreeNode((NodeLayout) nodes.iterator() >+ .next()); >+ if (subgraphRoot == null) { >+ return; >+ } >+ while (nodes.contains(subgraphRoot.getNode())) { >+ subgraphRoot = subgraphRoot.getParent(); >+ } >+ >+ TreeNode superRoot = tree.getSuperRoot(); >+ double triangleHeight = parameters.referenceHeight >+ * subgraphRoot.getHeight() / superRoot.getHeight(); >+ >+ int numOfNodes = superRoot.getNumOfDescendants(); >+ int numOfNodesWithChildren = numOfNodes - superRoot.getNumOfLeaves() >+ + 1; >+ double logBase = (numOfNodesWithChildren > 0) ? (double) numOfNodes >+ / numOfNodesWithChildren : 1; >+ // logarithm base is the average number of children for whole context >+ double triangleBaseModifier = (parameters.referenceBase - 1) >+ / log(superRoot.getNumOfLeaves(), logBase); >+ double triangleBase = parameters.referenceBase >+ + triangleBaseModifier >+ * log((double) subgraphRoot.getNumOfLeaves() >+ / superRoot.getNumOfLeaves(), logBase); >+ >+ if (parameters.direction == 0) { >+ parameters.direction = parameters.direction; >+ } >+ if (parameters.direction == TOP_DOWN >+ || parameters.direction == BOTTOM_UP) { >+ figure.setSize((int) (triangleBase + 0.5), >+ (int) (triangleHeight + 0.5)); >+ } else { >+ figure.setSize((int) (triangleHeight + 0.5), >+ (int) (triangleBase + 0.5)); >+ } >+ >+ int numOfNodesWithChildrenInSubgraph = nodes.size() >+ - subgraphRoot.getNumOfLeaves() + 1; >+ double avgNumOfChildrenInSugbraph = (numOfNodesWithChildrenInSubgraph > 0) ? (double) nodes >+ .size() >+ / numOfNodesWithChildrenInSubgraph >+ : 1; >+ int r = (int) (parameters.color.getRed() + ((double) 255 - parameters.color >+ .getRed()) >+ / avgNumOfChildrenInSugbraph); >+ int g = (int) (parameters.color.getGreen() + ((double) 255 - parameters.color >+ .getGreen()) >+ / avgNumOfChildrenInSugbraph); >+ int b = (int) (parameters.color.getBlue() + ((double) 255 - parameters.color >+ .getBlue()) >+ / avgNumOfChildrenInSugbraph); >+ figure.setBackgroundColor(new Color(parameters.color.getDevice(), r, g, >+ b)); >+ figure.setForegroundColor(parameters.color); >+ } >+ >+ public boolean isDirectionDependant() { >+ return true; >+ } >+ >+ public void setDirection(int direction) { >+ super.setDirection(direction); >+ if (parameters.direction == direction) { >+ return; >+ } >+ if (direction == TOP_DOWN || direction == BOTTOM_UP >+ || direction == LEFT_RIGHT || direction == RIGHT_LEFT) { >+ parameters.direction = direction; >+ updateFigure(); >+ } else { >+ throw new IllegalArgumentException("invalid direction"); >+ } >+ } >+ >+ /** >+ * Changes the color of the triangle visualizing this subgraph. >+ * >+ * @param color >+ * color to use >+ */ >+ public void setColor(Color color) { >+ parameters.color = color; >+ updateFigure(); >+ } >+} >Index: src/org/eclipse/zest/core/widgets/internal/AligningBendpointLocator.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/widgets/internal/AligningBendpointLocator.java,v >retrieving revision 1.3 >diff -u -r1.3 AligningBendpointLocator.java >--- src/org/eclipse/zest/core/widgets/internal/AligningBendpointLocator.java 12 Sep 2007 20:44:39 -0000 1.3 >+++ src/org/eclipse/zest/core/widgets/internal/AligningBendpointLocator.java 15 Mar 2010 03:49:02 -0000 >@@ -1,219 +1,236 @@ >-/******************************************************************************* >- * Copyright 2005-2006, 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.widgets.internal; >- >-import org.eclipse.draw2d.AbstractLocator; >-import org.eclipse.draw2d.Connection; >-import org.eclipse.draw2d.IFigure; >-import org.eclipse.draw2d.PositionConstants; >-import org.eclipse.draw2d.geometry.Dimension; >-import org.eclipse.draw2d.geometry.Point; >-import org.eclipse.draw2d.geometry.PointList; >- >-/** >- * A locator that finds the middle of a connection based on the bendpoints. >- * @author Del Myers >- * >- */ >-//@tag zest(bug(154391-ArcEnds(fix))) : use this locator to ensure that labels will be in the middle of connections. >-//@tag zest.bug.160368-ConnectionAlign : replaces MidBenpointLocator in order to have finer control over alignment. >-public class AligningBendpointLocator extends AbstractLocator { >- /** >- * "Vertical" alignment contstant. Figures should be placed in the middle >- * of the line. >- */ >- public static final int MIDDLE = 0; >- /** >- * "Vertical" alignment constant. Figures should be placed above the line. >- */ >- public static final int ABOVE = 1; >- /** >- * "Vertical" alignment constant. Figures should be placed below the line. >- */ >- public static final int BELOW = 2; >- >- /** >- * "Horizontal" alignment constant. Figures should be placed in the center >- * of the points on the line. >- */ >- public static final int CENTER = 0; >- >- /** >- * "Horizontal" alignment constant. Figures should be placed at the beginning >- * of the line. Figures will be anchored so that they have one end at the >- * beginning of the connection, not so that they are centered at the start >- * point. Which end of the figure is placed at that point will depend >- * on the direction of the first two points. >- */ >- public static final int BEGINNING = 1; >- >- /** >- * "Horizontal" alignment constant. Figures should be placed at the end of >- * the line. Figures will be anchored so that they have one end at the >- * beginning of the connection, not so that they are centered at the end >- * point. Which end of the figure is placed at that point will depend >- * on the direction of the last two points. >- */ >- public static final int END = 2; >- >- /** >- * "Horizontal" alignment constant. Figures should be centered between the >- * first two points on the connection. For connections with only two points, >- * this will behave the same as CENTER. >- */ >- public static final int CENTER_BEGINNING = 3; >- >- /** >- * "Horizontal" alignment constant. Figures should be centered between the >- * last two points on the connection. For connections with only two points, >- * this will behave the same as CENTER. >- */ >- public static final int CENTER_END = 4; >- private int horizontal; >- private int vertical; >- private Connection connection; >- /** >- * @param connection >- */ >- public AligningBendpointLocator(Connection connection) { >- this(connection, CENTER, MIDDLE); >- } >- >- public AligningBendpointLocator(Connection connection, int horizontal, int vertical) { >- this.connection = connection; >- this.horizontal = horizontal; >- this.vertical = vertical; >- } >- >- /* (non-Javadoc) >- * @see org.eclipse.draw2d.ConnectionLocator#getReferencePoint() >- */ >- protected Point getReferencePoint() { >- PointList points = getConnection().getPoints(); >- Point p = points.getMidpoint().getCopy(); >- PointList tempPoints = new PointList(); >- switch (horizontal) { >- case BEGINNING: >- p = points.getFirstPoint().getCopy(); >- break; >- case END: >- p = points.getLastPoint().getCopy(); >- break; >- case CENTER_BEGINNING: >- tempPoints.addPoint(points.getFirstPoint().getCopy()); >- tempPoints.addPoint(points.getPoint(1).getCopy()); >- p = tempPoints.getMidpoint().getCopy(); >- break; >- case CENTER_END: >- tempPoints = new PointList(); >- int s = points.size(); >- tempPoints.addPoint(points.getLastPoint().getCopy()); >- tempPoints.addPoint(points.getPoint(s-2).getCopy()); >- p = tempPoints.getMidpoint().getCopy(); >- case CENTER: >- default: >- p = points.getMidpoint().getCopy(); >- } >- return p; >- } >- /** >- * Recalculates the position of the figure and returns the updated bounds. >- * @param target The figure to relocate >- */ >- public void relocate(IFigure target) { >- Dimension prefSize = target.getPreferredSize(); >- Point center = getReferencePoint(); >- calculatePosition(); >- //@tag zest(bug(GEFProblem)) : there seems to be a bug in GEF that if the following is done, then labels get printed in the wrong location >- //target.translateToRelative(center); >- target.setBounds(getNewBounds(prefSize, center)); >- } >- >- /** >- * Translates the center point depending on the horizontal and veritical >- * alignments based on the given bounds. >- * @param center >- */ >- private void calculatePosition() { >- PointList points = getConnection().getPoints(); >- int position = 0; >- switch(horizontal) { >- case BEGINNING: >- Point first = points.getFirstPoint(); >- Point next = points.getPoint(1); >- if (first.x <= next.x) >- position |= PositionConstants.EAST; >- else >- position |= PositionConstants.WEST; >- break; >- case END: >- Point last = points.getLastPoint(); >- int s = points.size(); >- Point before = points.getPoint(s-1); >- if (last.x <= before.x) >- position |= PositionConstants.EAST; >- else >- position |= PositionConstants.WEST; >- break; >- } >- if (position == 0) position = PositionConstants.CENTER; >- switch (vertical) { >- case ABOVE: >- position |= PositionConstants.NORTH; >- break; >- case BELOW: >- position |= PositionConstants.SOUTH; >- break; >- case MIDDLE: >- position |= PositionConstants.MIDDLE; >- } >- setRelativePosition(position); >- >- } >- >- /** >- * @return the horizontal alignment. >- */ >- public int getHorizontalAlignment() { >- return horizontal; >- } >- >- /** >- * @param horizontal the horizontal alignment to set. One of CENTER, >- * BEGINNING, END, CENTER_BEGINNING, or CENTER_END. >- */ >- public void setHorizontalAlignment(int horizontal) { >- this.horizontal = horizontal; >- } >- >- /** >- * @param vertical the vertical alignment to set. One of ABOVE, MIDDLE, or >- * BELOW. >- */ >- public void setVerticalAlginment(int vertical) { >- this.vertical = vertical; >- } >- >- /** >- * @return the vertical alginment. >- */ >- public int getVerticalAlignment() { >- return vertical; >- } >- >- /** >- * @return the connection >- */ >- public Connection getConnection() { >- return connection; >- } >- >-} >+/******************************************************************************* >+ * Copyright 2005-2010, 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.widgets.internal; >+ >+import org.eclipse.draw2d.AbstractLocator; >+import org.eclipse.draw2d.Connection; >+import org.eclipse.draw2d.IFigure; >+import org.eclipse.draw2d.PositionConstants; >+import org.eclipse.draw2d.geometry.Dimension; >+import org.eclipse.draw2d.geometry.Point; >+import org.eclipse.draw2d.geometry.PointList; >+ >+/** >+ * A locator that finds the middle of a connection based on the bendpoints. >+ * >+ * @author Del Myers >+ * >+ */ >+// @tag zest(bug(154391-ArcEnds(fix))) : use this locator to ensure that labels >+// will be in the middle of connections. >+// @tag zest.bug.160368-ConnectionAlign : replaces MidBenpointLocator in order >+// to have finer control over alignment. >+public class AligningBendpointLocator extends AbstractLocator { >+ /** >+ * "Vertical" alignment contstant. Figures should be placed in the middle of >+ * the line. >+ */ >+ public static final int MIDDLE = 0; >+ /** >+ * "Vertical" alignment constant. Figures should be placed above the line. >+ */ >+ public static final int ABOVE = 1; >+ /** >+ * "Vertical" alignment constant. Figures should be placed below the line. >+ */ >+ public static final int BELOW = 2; >+ >+ /** >+ * "Horizontal" alignment constant. Figures should be placed in the center >+ * of the points on the line. >+ */ >+ public static final int CENTER = 0; >+ >+ /** >+ * "Horizontal" alignment constant. Figures should be placed at the >+ * beginning of the line. Figures will be anchored so that they have one end >+ * at the beginning of the connection, not so that they are centered at the >+ * start point. Which end of the figure is placed at that point will depend >+ * on the direction of the first two points. >+ */ >+ public static final int BEGINNING = 1; >+ >+ /** >+ * "Horizontal" alignment constant. Figures should be placed at the end of >+ * the line. Figures will be anchored so that they have one end at the >+ * beginning of the connection, not so that they are centered at the end >+ * point. Which end of the figure is placed at that point will depend on the >+ * direction of the last two points. >+ */ >+ public static final int END = 2; >+ >+ /** >+ * "Horizontal" alignment constant. Figures should be centered between the >+ * first two points on the connection. For connections with only two points, >+ * this will behave the same as CENTER. >+ */ >+ public static final int CENTER_BEGINNING = 3; >+ >+ /** >+ * "Horizontal" alignment constant. Figures should be centered between the >+ * last two points on the connection. For connections with only two points, >+ * this will behave the same as CENTER. >+ */ >+ public static final int CENTER_END = 4; >+ private int horizontal; >+ private int vertical; >+ private Connection connection; >+ >+ /** >+ * @param connection >+ */ >+ public AligningBendpointLocator(Connection connection) { >+ this(connection, CENTER, MIDDLE); >+ } >+ >+ public AligningBendpointLocator(Connection connection, int horizontal, >+ int vertical) { >+ this.connection = connection; >+ this.horizontal = horizontal; >+ this.vertical = vertical; >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see org.eclipse.draw2d.ConnectionLocator#getReferencePoint() >+ */ >+ protected Point getReferencePoint() { >+ PointList points = getConnection().getPoints(); >+ Point p = points.getMidpoint().getCopy(); >+ PointList tempPoints = new PointList(); >+ switch (horizontal) { >+ case BEGINNING: >+ p = points.getFirstPoint().getCopy(); >+ break; >+ case END: >+ p = points.getLastPoint().getCopy(); >+ break; >+ case CENTER_BEGINNING: >+ tempPoints.addPoint(points.getFirstPoint().getCopy()); >+ tempPoints.addPoint(points.getPoint(1).getCopy()); >+ p = tempPoints.getMidpoint().getCopy(); >+ break; >+ case CENTER_END: >+ tempPoints = new PointList(); >+ int s = points.size(); >+ tempPoints.addPoint(points.getLastPoint().getCopy()); >+ tempPoints.addPoint(points.getPoint(s - 2).getCopy()); >+ p = tempPoints.getMidpoint().getCopy(); >+ case CENTER: >+ default: >+ p = points.getMidpoint().getCopy(); >+ } >+ return p; >+ } >+ >+ /** >+ * Recalculates the position of the figure and returns the updated bounds. >+ * >+ * @param target >+ * The figure to relocate >+ */ >+ public void relocate(IFigure target) { >+ Dimension prefSize = target.getPreferredSize(); >+ Point center = getReferencePoint(); >+ calculatePosition(); >+ // @tag zest(bug(GEFProblem)) : there seems to be a bug in GEF that if >+ // the following is done, then labels get printed in the wrong location >+ // target.translateToRelative(center); >+ target.setBounds(getNewBounds(prefSize, center)); >+ } >+ >+ /** >+ * Translates the center point depending on the horizontal and veritical >+ * alignments based on the given bounds. >+ * >+ * @param center >+ */ >+ private void calculatePosition() { >+ PointList points = getConnection().getPoints(); >+ int position = 0; >+ switch (horizontal) { >+ case BEGINNING: >+ Point first = points.getFirstPoint(); >+ Point next = points.getPoint(1); >+ if (first.x <= next.x) { >+ position |= PositionConstants.EAST; >+ } else { >+ position |= PositionConstants.WEST; >+ } >+ break; >+ case END: >+ Point last = points.getLastPoint(); >+ int s = points.size(); >+ Point before = points.getPoint(s - 1); >+ if (last.x <= before.x) { >+ position |= PositionConstants.EAST; >+ } else { >+ position |= PositionConstants.WEST; >+ } >+ break; >+ } >+ if (position == 0) { >+ position = PositionConstants.CENTER; >+ } >+ switch (vertical) { >+ case ABOVE: >+ position |= PositionConstants.NORTH; >+ break; >+ case BELOW: >+ position |= PositionConstants.SOUTH; >+ break; >+ case MIDDLE: >+ position |= PositionConstants.MIDDLE; >+ } >+ setRelativePosition(position); >+ >+ } >+ >+ /** >+ * @return the horizontal alignment. >+ */ >+ public int getHorizontalAlignment() { >+ return horizontal; >+ } >+ >+ /** >+ * @param horizontal >+ * the horizontal alignment to set. One of CENTER, BEGINNING, >+ * END, CENTER_BEGINNING, or CENTER_END. >+ */ >+ public void setHorizontalAlignment(int horizontal) { >+ this.horizontal = horizontal; >+ } >+ >+ /** >+ * @param vertical >+ * the vertical alignment to set. One of ABOVE, MIDDLE, or BELOW. >+ */ >+ public void setVerticalAlginment(int vertical) { >+ this.vertical = vertical; >+ } >+ >+ /** >+ * @return the vertical alginment. >+ */ >+ public int getVerticalAlignment() { >+ return vertical; >+ } >+ >+ /** >+ * @return the connection >+ */ >+ public Connection getConnection() { >+ return connection; >+ } >+ >+} >Index: src/org/eclipse/zest/core/widgets/internal/AspectRatioFreeformLayer.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/widgets/internal/AspectRatioFreeformLayer.java,v >retrieving revision 1.4 >diff -u -r1.4 AspectRatioFreeformLayer.java >--- src/org/eclipse/zest/core/widgets/internal/AspectRatioFreeformLayer.java 12 Sep 2007 20:44:39 -0000 1.4 >+++ src/org/eclipse/zest/core/widgets/internal/AspectRatioFreeformLayer.java 15 Mar 2010 03:49:02 -0000 >@@ -1,231 +1,235 @@ >-/******************************************************************************* >- * Copyright 2005-2006, 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.widgets.internal; >- >-import org.eclipse.draw2d.FreeformFigure; >-import org.eclipse.draw2d.FreeformLayer; >-import org.eclipse.draw2d.FreeformLayout; >-import org.eclipse.draw2d.Graphics; >-import org.eclipse.draw2d.MarginBorder; >-import org.eclipse.draw2d.ScalableFigure; >-import org.eclipse.draw2d.geometry.Dimension; >-import org.eclipse.draw2d.geometry.Point; >-import org.eclipse.draw2d.geometry.PointList; >-import org.eclipse.draw2d.geometry.PrecisionDimension; >-import org.eclipse.draw2d.geometry.PrecisionPoint; >-import org.eclipse.draw2d.geometry.PrecisionRectangle; >-import org.eclipse.draw2d.geometry.Rectangle; >-import org.eclipse.draw2d.geometry.Translatable; >-import org.eclipse.draw2d.text.CaretInfo; >- >-//@tag zest.bug.156286-Scaling.fix : make this implement scalable figure so that a zoom manager can be used on GraphEditParts. >-public class AspectRatioFreeformLayer extends FreeformLayer implements ScalableFigure, FreeformFigure { >- >- private double widthScale = 1.0; >- private double heigthScale = 1.0; >- >- public AspectRatioFreeformLayer(String debugLabel) { >- widthScale = 1D; >- heigthScale = 1D; >- setLayoutManager(new FreeformLayout()); >- setBorder(new MarginBorder(5)); >- >- //setOpaque(false); >- } >- >- protected boolean isValidationRoot() { >- return true; >- } >- >- public void setScale(double wScale, double hScale) { >- this.widthScale = wScale; >- this.heigthScale = hScale; >- } >- >- public double getWidthScale() { >- return this.widthScale; >- } >- >- public double getHeightScale() { >- return this.heigthScale; >- } >- >- /* >- public boolean isCoordinateSystem() { >- // TODO Auto-generated method stub >- return true; >- } >- */ >- >- public double getScale() { >- // TODO Auto-generated method stub >- throw new RuntimeException("Operation not supported"); >- // return this.widthScale; >- >- //throw new RuntimeException("Operation Not supported"); >- } >- >- public void setScale(double scale) { >- //super.setScale( scale ); >- this.widthScale = scale; >- this.heigthScale = scale; >- revalidate(); >- repaint(); >- //System.out.println("Operation not supported"); >- //throw new RuntimeException("Operation not supported"); >- } >- >- /** >- * @see org.eclipse.draw2d.Figure#getClientArea() >- */ >- >- public Rectangle getClientArea(Rectangle rect) { >- //return super.getClientArea(rect); >- >- rect.width /= widthScale; >- rect.height /= heigthScale; >- rect.x /= widthScale; >- rect.y /= heigthScale; >- return rect; >- } >- >- public Dimension getPreferredSize(int wHint, int hHint) { >- Dimension d = super.getPreferredSize(wHint, hHint); >- int w = getInsets().getWidth(); >- int h = getInsets().getHeight(); >- return d.getExpanded(-w, -h).scale(widthScale, heigthScale).expand(w, h); >- } >- >- public void translateFromParent(Translatable t) { >- super.translateFromParent(t); >- //t.performScale(1/widthScale); >- >- if (t instanceof PrecisionRectangle) { >- PrecisionRectangle r = (PrecisionRectangle) t; >- r.preciseX *= 1 / widthScale; >- r.preciseY *= 1 / heigthScale; >- r.preciseWidth *= 1 / widthScale; >- r.preciseHeight *= 1 / heigthScale; >- r.updateInts(); >- } else if (t instanceof Rectangle) { >- Rectangle r = (Rectangle) t; >- r.scale(1 / widthScale, 1 / heigthScale); >- } else if (t instanceof CaretInfo) { >- CaretInfo c = (CaretInfo) t; >- c.performScale(1 / heigthScale); >- } else if (t instanceof PrecisionDimension) { >- PrecisionDimension d = (PrecisionDimension) t; >- d.preciseWidth *= 1 / widthScale; >- d.preciseHeight *= 1 / heigthScale; >- d.updateInts(); >- } else if (t instanceof Dimension) { >- Dimension d = (Dimension) t; >- d.scale(1 / widthScale, 1 / heigthScale); >- } else if (t instanceof PrecisionPoint) { >- PrecisionPoint p = (PrecisionPoint) t; >- p.preciseX *= 1 / widthScale; >- p.preciseY *= 1 / heigthScale; >- p.updateInts(); >- } else if (t instanceof Point) { >- Point p = (Point) t; >- p.scale(1 / widthScale, 1 / heigthScale); >- } else if (t instanceof PointList) { >- throw new RuntimeException("PointList not supported in AspectRatioScale"); >- } else { >- throw new RuntimeException(t.toString() + " not supported in AspectRatioScale"); >- } >- >- //t.performScale(1/widthScale); >- } >- >- public void translateToParent(Translatable t) { >- //t.performScale(widthScale); >- >- if (t instanceof PrecisionRectangle) { >- PrecisionRectangle r = (PrecisionRectangle) t; >- r.preciseX *= widthScale; >- r.preciseY *= heigthScale; >- r.preciseWidth *= widthScale; >- r.preciseHeight *= heigthScale; >- r.updateInts(); >- } else if (t instanceof Rectangle) { >- Rectangle r = (Rectangle) t; >- //r.performScale(widthScale); >- r.scale(widthScale, heigthScale); >- } else if (t instanceof CaretInfo) { >- CaretInfo c = (CaretInfo) t; >- c.performScale(heigthScale); >- } else if (t instanceof PrecisionDimension) { >- PrecisionDimension d = (PrecisionDimension) t; >- d.preciseWidth *= widthScale; >- d.preciseHeight *= heigthScale; >- d.updateInts(); >- } else if (t instanceof Dimension) { >- Dimension d = (Dimension) t; >- d.scale(widthScale, heigthScale); >- } else if (t instanceof PrecisionPoint) { >- PrecisionPoint p = (PrecisionPoint) t; >- p.preciseX *= widthScale; >- p.preciseY *= heigthScale; >- p.updateInts(); >- } else if (t instanceof Point) { >- Point p = (Point) t; >- p.scale(widthScale, heigthScale); >- } else if (t instanceof PointList) { >- throw new RuntimeException("PointList not supported in AspectRatioScale"); >- } else { >- throw new RuntimeException(t.toString() + " not supported in AspectRatioScale"); >- } >- >- super.translateToParent(t); >- } >- >- //protected boolean useLocalCoordinates() { >- // return true; >- //} >- >- protected void paintClientArea(Graphics graphics) { >- >- if (getChildren().isEmpty()) { >- return; >- } >- >- XYScaledGraphics g = null; >- boolean disposeGraphics = false; >- if (graphics instanceof XYScaledGraphics) { >- g = (XYScaledGraphics) graphics; >- } else { >- g = new XYScaledGraphics(graphics); >- disposeGraphics = true; >- } >- >- boolean optimizeClip = getBorder() == null || getBorder().isOpaque(); >- if (!optimizeClip) { >- g.clipRect(getBounds().getCropped(getInsets())); >- } >- >- //g.translate((int)(getBounds().x + getInsets().left) , >- // (int)(getBounds().y + getInsets().top) ); >- >- g.scale(widthScale, heigthScale); >- //g.scale(widthScale); >- >- //g.scale(widthScale); >- g.pushState(); >- paintChildren(g); >- g.popState(); >- if (disposeGraphics) { >- g.dispose(); >- graphics.restoreState(); >- } >- >- } >- >-} >+/******************************************************************************* >+ * Copyright 2005-2010, 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.widgets.internal; >+ >+import org.eclipse.draw2d.FreeformFigure; >+import org.eclipse.draw2d.FreeformLayer; >+import org.eclipse.draw2d.FreeformLayout; >+import org.eclipse.draw2d.Graphics; >+import org.eclipse.draw2d.MarginBorder; >+import org.eclipse.draw2d.ScalableFigure; >+import org.eclipse.draw2d.geometry.Dimension; >+import org.eclipse.draw2d.geometry.Point; >+import org.eclipse.draw2d.geometry.PointList; >+import org.eclipse.draw2d.geometry.PrecisionDimension; >+import org.eclipse.draw2d.geometry.PrecisionPoint; >+import org.eclipse.draw2d.geometry.PrecisionRectangle; >+import org.eclipse.draw2d.geometry.Rectangle; >+import org.eclipse.draw2d.geometry.Translatable; >+import org.eclipse.draw2d.text.CaretInfo; >+ >+//@tag zest.bug.156286-Scaling.fix : make this implement scalable figure so that a zoom manager can be used on GraphEditParts. >+public class AspectRatioFreeformLayer extends FreeformLayer implements >+ ScalableFigure, FreeformFigure { >+ >+ private double widthScale = 1.0; >+ private double heigthScale = 1.0; >+ >+ public AspectRatioFreeformLayer(String debugLabel) { >+ widthScale = 1D; >+ heigthScale = 1D; >+ setLayoutManager(new FreeformLayout()); >+ setBorder(new MarginBorder(5)); >+ >+ // setOpaque(false); >+ } >+ >+ protected boolean isValidationRoot() { >+ return true; >+ } >+ >+ public void setScale(double wScale, double hScale) { >+ this.widthScale = wScale; >+ this.heigthScale = hScale; >+ } >+ >+ public double getWidthScale() { >+ return this.widthScale; >+ } >+ >+ public double getHeightScale() { >+ return this.heigthScale; >+ } >+ >+ /* >+ * public boolean isCoordinateSystem() { // TODO Auto-generated method stub >+ * return true; } >+ */ >+ >+ public double getScale() { >+ // TODO Auto-generated method stub >+ throw new RuntimeException("Operation not supported"); >+ // return this.widthScale; >+ >+ // throw new RuntimeException("Operation Not supported"); >+ } >+ >+ public void setScale(double scale) { >+ // super.setScale( scale ); >+ this.widthScale = scale; >+ this.heigthScale = scale; >+ revalidate(); >+ repaint(); >+ // System.out.println("Operation not supported"); >+ // throw new RuntimeException("Operation not supported"); >+ } >+ >+ /** >+ * @see org.eclipse.draw2d.Figure#getClientArea() >+ */ >+ >+ public Rectangle getClientArea(Rectangle rect) { >+ // return super.getClientArea(rect); >+ >+ rect.width /= widthScale; >+ rect.height /= heigthScale; >+ rect.x /= widthScale; >+ rect.y /= heigthScale; >+ return rect; >+ } >+ >+ public Dimension getPreferredSize(int wHint, int hHint) { >+ Dimension d = super.getPreferredSize(wHint, hHint); >+ int w = getInsets().getWidth(); >+ int h = getInsets().getHeight(); >+ return d.getExpanded(-w, -h).scale(widthScale, heigthScale) >+ .expand(w, h); >+ } >+ >+ public void translateFromParent(Translatable t) { >+ super.translateFromParent(t); >+ // t.performScale(1/widthScale); >+ >+ if (t instanceof PrecisionRectangle) { >+ PrecisionRectangle r = (PrecisionRectangle) t; >+ r.preciseX *= 1 / widthScale; >+ r.preciseY *= 1 / heigthScale; >+ r.preciseWidth *= 1 / widthScale; >+ r.preciseHeight *= 1 / heigthScale; >+ r.updateInts(); >+ } else if (t instanceof Rectangle) { >+ Rectangle r = (Rectangle) t; >+ r.scale(1 / widthScale, 1 / heigthScale); >+ } else if (t instanceof CaretInfo) { >+ CaretInfo c = (CaretInfo) t; >+ c.performScale(1 / heigthScale); >+ } else if (t instanceof PrecisionDimension) { >+ PrecisionDimension d = (PrecisionDimension) t; >+ d.preciseWidth *= 1 / widthScale; >+ d.preciseHeight *= 1 / heigthScale; >+ d.updateInts(); >+ } else if (t instanceof Dimension) { >+ Dimension d = (Dimension) t; >+ d.scale(1 / widthScale, 1 / heigthScale); >+ } else if (t instanceof PrecisionPoint) { >+ PrecisionPoint p = (PrecisionPoint) t; >+ p.preciseX *= 1 / widthScale; >+ p.preciseY *= 1 / heigthScale; >+ p.updateInts(); >+ } else if (t instanceof Point) { >+ Point p = (Point) t; >+ p.scale(1 / widthScale, 1 / heigthScale); >+ } else if (t instanceof PointList) { >+ throw new RuntimeException( >+ "PointList not supported in AspectRatioScale"); >+ } else { >+ throw new RuntimeException(t.toString() >+ + " not supported in AspectRatioScale"); >+ } >+ >+ // t.performScale(1/widthScale); >+ } >+ >+ public void translateToParent(Translatable t) { >+ // t.performScale(widthScale); >+ >+ if (t instanceof PrecisionRectangle) { >+ PrecisionRectangle r = (PrecisionRectangle) t; >+ r.preciseX *= widthScale; >+ r.preciseY *= heigthScale; >+ r.preciseWidth *= widthScale; >+ r.preciseHeight *= heigthScale; >+ r.updateInts(); >+ } else if (t instanceof Rectangle) { >+ Rectangle r = (Rectangle) t; >+ // r.performScale(widthScale); >+ r.scale(widthScale, heigthScale); >+ } else if (t instanceof CaretInfo) { >+ CaretInfo c = (CaretInfo) t; >+ c.performScale(heigthScale); >+ } else if (t instanceof PrecisionDimension) { >+ PrecisionDimension d = (PrecisionDimension) t; >+ d.preciseWidth *= widthScale; >+ d.preciseHeight *= heigthScale; >+ d.updateInts(); >+ } else if (t instanceof Dimension) { >+ Dimension d = (Dimension) t; >+ d.scale(widthScale, heigthScale); >+ } else if (t instanceof PrecisionPoint) { >+ PrecisionPoint p = (PrecisionPoint) t; >+ p.preciseX *= widthScale; >+ p.preciseY *= heigthScale; >+ p.updateInts(); >+ } else if (t instanceof Point) { >+ Point p = (Point) t; >+ p.scale(widthScale, heigthScale); >+ } else if (t instanceof PointList) { >+ throw new RuntimeException( >+ "PointList not supported in AspectRatioScale"); >+ } else { >+ throw new RuntimeException(t.toString() >+ + " not supported in AspectRatioScale"); >+ } >+ >+ super.translateToParent(t); >+ } >+ >+ // protected boolean useLocalCoordinates() { >+ // return true; >+ // } >+ >+ protected void paintClientArea(Graphics graphics) { >+ >+ if (getChildren().isEmpty()) { >+ return; >+ } >+ >+ XYScaledGraphics g = null; >+ boolean disposeGraphics = false; >+ if (graphics instanceof XYScaledGraphics) { >+ g = (XYScaledGraphics) graphics; >+ } else { >+ g = new XYScaledGraphics(graphics); >+ disposeGraphics = true; >+ } >+ >+ boolean optimizeClip = getBorder() == null || getBorder().isOpaque(); >+ if (!optimizeClip) { >+ g.clipRect(getBounds().getCropped(getInsets())); >+ } >+ >+ // g.translate((int)(getBounds().x + getInsets().left) , >+ // (int)(getBounds().y + getInsets().top) ); >+ >+ g.scale(widthScale, heigthScale); >+ // g.scale(widthScale); >+ >+ // g.scale(widthScale); >+ g.pushState(); >+ paintChildren(g); >+ g.popState(); >+ if (disposeGraphics) { >+ g.dispose(); >+ graphics.restoreState(); >+ } >+ >+ } >+ >+} >Index: src/org/eclipse/zest/core/widgets/internal/CachedLabel.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/widgets/internal/CachedLabel.java,v >retrieving revision 1.5 >diff -u -r1.5 CachedLabel.java >--- src/org/eclipse/zest/core/widgets/internal/CachedLabel.java 12 Sep 2007 20:44:39 -0000 1.5 >+++ src/org/eclipse/zest/core/widgets/internal/CachedLabel.java 15 Mar 2010 03:49:02 -0000 >@@ -1,247 +1,255 @@ >-/******************************************************************************* >- * Copyright 2005-2006, 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.widgets.internal; >- >-import org.eclipse.draw2d.Animation; >-import org.eclipse.draw2d.ColorConstants; >-import org.eclipse.draw2d.Graphics; >-import org.eclipse.draw2d.Label; >-import org.eclipse.draw2d.SWTGraphics; >-import org.eclipse.draw2d.ScaledGraphics; >-import org.eclipse.draw2d.geometry.Point; >-import org.eclipse.draw2d.geometry.Rectangle; >-import org.eclipse.swt.graphics.Color; >-import org.eclipse.swt.graphics.Font; >-import org.eclipse.swt.graphics.GC; >-import org.eclipse.swt.graphics.Image; >-import org.eclipse.swt.widgets.Display; >- >-/** >- * A cached label to improve performance of text drawing under linux >- * >- * @author Ian Bull >- * >- */ >-public abstract class CachedLabel extends Label { >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.draw2d.Label#paintFigure(org.eclipse.draw2d.Graphics) >- */ >- Image cachedImage = null; >- boolean cacheLabel = false; >- boolean invalidationRequired = false; >- >- /** >- * CachedLabel constructor. >- * >- * @param cacheLabel >- * Should the label be cached, or should the text be re-layedout >- * each time >- */ >- public CachedLabel(boolean cacheLabel) { >- this.cacheLabel = cacheLabel; >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.draw2d.Label#setIcon(org.eclipse.swt.graphics.Image) >- */ >- public void setIcon(Image image) { >- updateInvalidation(); >- super.setIcon(image); >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.draw2d.Figure#setForegroundColor(org.eclipse.swt.graphics.Color) >- */ >- public void setForegroundColor(Color fg) { >- updateInvalidation(); >- super.setForegroundColor(fg); >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.draw2d.Figure#setBackgroundColor(org.eclipse.swt.graphics.Color) >- */ >- public void setBackgroundColor(Color bg) { >- updateInvalidation(); >- super.setBackgroundColor(bg); >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.draw2d.Figure#setFont(org.eclipse.swt.graphics.Font) >- */ >- public void setFont(Font f) { >- updateInvalidation(); >- super.setFont(f); >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.draw2d.Label#setText(java.lang.String) >- */ >- public void setText(String s) { >- updateInvalidation(); >- super.setText(s); >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.draw2d.Figure#setSize(int, int) >- */ >- public void setSize(int w, int h) { >- updateInvalidation(); >- >- if (cachedImage != null && shouldInvalidateCache()) { >- cleanImage(); >- } >- super.setSize(w, h); >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.draw2d.Figure#setBounds(org.eclipse.draw2d.geometry.Rectangle) >- */ >- public void setBounds(Rectangle rect) { >- boolean resize = (rect.width != bounds.width) || (rect.height != bounds.height); >- >- if (resize && Animation.isAnimating()) { >- updateInvalidation(); >- } >- if (resize && shouldInvalidateCache() && cachedImage != null) { >- cleanImage(); >- } >- >- super.setBounds(rect); >- } >- >- /** >- * Override this method to return the background colour for the text Note: >- * Text must have a background color since it is being stored in an image >- * (You can set it to white if you want) >- * >- * @return >- */ >- protected abstract Color getBackgroundTextColor(); >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.draw2d.Label#paintFigure(org.eclipse.draw2d.Graphics) >- */ >- static Rectangle tempRect = new Rectangle(); >- >- protected void paintFigure(Graphics graphics) { >- if (graphics.getClass() == ScaledGraphics.class) { >- if (((ScaledGraphics) graphics).getAbsoluteScale() < 0.30) { >- return; >- } >- } >- if (!cacheLabel) { >- if (isOpaque()) { >- super.paintFigure(graphics); >- } >- Rectangle bounds = getBounds(); >- graphics.translate(bounds.x, bounds.y); >- if (getIcon() != null) { >- graphics.drawImage(getIcon(), getIconLocation()); >- } >- if (!isEnabled()) { >- graphics.translate(1, 1); >- graphics.setForegroundColor(ColorConstants.buttonLightest); >- graphics.drawText(getSubStringText(), getTextLocation()); >- graphics.translate(-1, -1); >- graphics.setForegroundColor(ColorConstants.buttonDarker); >- } >- graphics.drawText(getText(), getTextLocation()); >- graphics.translate(-bounds.x, -bounds.y); >- return; >- } >- >- if (isOpaque()) { >- graphics.fillRectangle(getBounds()); >- } >- Rectangle bounds = getBounds(); >- graphics.translate(bounds.x, bounds.y); >- >- Image icon = getIcon(); >- >- if (icon != null) { >- graphics.drawImage(icon, getIconLocation()); >- } >- >- int width = getSubStringTextSize().width; >- int height = getSubStringTextSize().height; >- >- if (cachedImage == null || shouldInvalidateCache()) { >- invalidationRequired = false; >- cleanImage(); >- cachedImage = new Image(Display.getCurrent(), width, height); >- >- // @tag TODO : Dispose of the image properly >- //ZestPlugin.getDefault().addImage(cachedImage.toString(), cachedImage); >- >- GC gc = new GC(cachedImage); >- >- Graphics graphics2 = new SWTGraphics(gc); >- graphics2.setBackgroundColor(getBackgroundTextColor()); >- graphics2.fillRectangle(0, 0, width, height); >- graphics2.setForegroundColor(getForegroundColor()); >- //graphics2.drawText(getSubStringText(), new Point(0, 0)); >- graphics2.drawText(getText(), new Point(0, 0)); >- gc.dispose(); >- >- } >- graphics.drawImage(cachedImage, getTextLocation()); >- graphics.translate(-bounds.x, -bounds.y); >- this.paintBorder(graphics); >- >- } >- >- /** >- * Determine if the image should be remade or not >- * >- * @return >- */ >- private boolean shouldInvalidateCache() { >- if (invalidationRequired && !Animation.isAnimating()) { >- return true; >- } else { >- return false; >- } >- } >- >- /** >- * Notifies the cache that the image will need updating. >- */ >- private void updateInvalidation() { >- invalidationRequired = true; >- } >- >- protected void cleanImage() { >- if (cachedImage != null) { >- >- //ZestPlugin.getDefault().removeImage(cachedImage.toString()); >- cachedImage.dispose(); >- cachedImage = null; >- } >- } >-} >+/******************************************************************************* >+ * Copyright 2005-2010, 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.widgets.internal; >+ >+import org.eclipse.draw2d.Animation; >+import org.eclipse.draw2d.ColorConstants; >+import org.eclipse.draw2d.Graphics; >+import org.eclipse.draw2d.Label; >+import org.eclipse.draw2d.SWTGraphics; >+import org.eclipse.draw2d.ScaledGraphics; >+import org.eclipse.draw2d.geometry.Point; >+import org.eclipse.draw2d.geometry.Rectangle; >+import org.eclipse.swt.graphics.Color; >+import org.eclipse.swt.graphics.Font; >+import org.eclipse.swt.graphics.GC; >+import org.eclipse.swt.graphics.Image; >+import org.eclipse.swt.widgets.Display; >+ >+/** >+ * A cached label to improve performance of text drawing under linux >+ * >+ * @author Ian Bull >+ * >+ */ >+public abstract class CachedLabel extends Label { >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see org.eclipse.draw2d.Label#paintFigure(org.eclipse.draw2d.Graphics) >+ */ >+ Image cachedImage = null; >+ boolean cacheLabel = false; >+ boolean invalidationRequired = false; >+ >+ /** >+ * CachedLabel constructor. >+ * >+ * @param cacheLabel >+ * Should the label be cached, or should the text be re-layedout >+ * each time >+ */ >+ public CachedLabel(boolean cacheLabel) { >+ this.cacheLabel = cacheLabel; >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see org.eclipse.draw2d.Label#setIcon(org.eclipse.swt.graphics.Image) >+ */ >+ public void setIcon(Image image) { >+ updateInvalidation(); >+ super.setIcon(image); >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see >+ * org.eclipse.draw2d.Figure#setForegroundColor(org.eclipse.swt.graphics >+ * .Color) >+ */ >+ public void setForegroundColor(Color fg) { >+ updateInvalidation(); >+ super.setForegroundColor(fg); >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see >+ * org.eclipse.draw2d.Figure#setBackgroundColor(org.eclipse.swt.graphics >+ * .Color) >+ */ >+ public void setBackgroundColor(Color bg) { >+ updateInvalidation(); >+ super.setBackgroundColor(bg); >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see org.eclipse.draw2d.Figure#setFont(org.eclipse.swt.graphics.Font) >+ */ >+ public void setFont(Font f) { >+ updateInvalidation(); >+ super.setFont(f); >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see org.eclipse.draw2d.Label#setText(java.lang.String) >+ */ >+ public void setText(String s) { >+ updateInvalidation(); >+ super.setText(s); >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see org.eclipse.draw2d.Figure#setSize(int, int) >+ */ >+ public void setSize(int w, int h) { >+ updateInvalidation(); >+ >+ if (cachedImage != null && shouldInvalidateCache()) { >+ cleanImage(); >+ } >+ super.setSize(w, h); >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see >+ * org.eclipse.draw2d.Figure#setBounds(org.eclipse.draw2d.geometry.Rectangle >+ * ) >+ */ >+ public void setBounds(Rectangle rect) { >+ boolean resize = (rect.width != bounds.width) >+ || (rect.height != bounds.height); >+ >+ if (resize && Animation.isAnimating()) { >+ updateInvalidation(); >+ } >+ if (resize && shouldInvalidateCache() && cachedImage != null) { >+ cleanImage(); >+ } >+ >+ super.setBounds(rect); >+ } >+ >+ /** >+ * Override this method to return the background colour for the text Note: >+ * Text must have a background color since it is being stored in an image >+ * (You can set it to white if you want) >+ * >+ * @return >+ */ >+ protected abstract Color getBackgroundTextColor(); >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see org.eclipse.draw2d.Label#paintFigure(org.eclipse.draw2d.Graphics) >+ */ >+ static Rectangle tempRect = new Rectangle(); >+ >+ protected void paintFigure(Graphics graphics) { >+ if (graphics.getClass() == ScaledGraphics.class) { >+ if (((ScaledGraphics) graphics).getAbsoluteScale() < 0.30) { >+ return; >+ } >+ } >+ if (!cacheLabel) { >+ if (isOpaque()) { >+ super.paintFigure(graphics); >+ } >+ Rectangle bounds = getBounds(); >+ graphics.translate(bounds.x, bounds.y); >+ if (getIcon() != null) { >+ graphics.drawImage(getIcon(), getIconLocation()); >+ } >+ if (!isEnabled()) { >+ graphics.translate(1, 1); >+ graphics.setForegroundColor(ColorConstants.buttonLightest); >+ graphics.drawText(getSubStringText(), getTextLocation()); >+ graphics.translate(-1, -1); >+ graphics.setForegroundColor(ColorConstants.buttonDarker); >+ } >+ graphics.drawText(getText(), getTextLocation()); >+ graphics.translate(-bounds.x, -bounds.y); >+ return; >+ } >+ >+ if (isOpaque()) { >+ graphics.fillRectangle(getBounds()); >+ } >+ Rectangle bounds = getBounds(); >+ graphics.translate(bounds.x, bounds.y); >+ >+ Image icon = getIcon(); >+ >+ if (icon != null) { >+ graphics.drawImage(icon, getIconLocation()); >+ } >+ >+ int width = getSubStringTextSize().width; >+ int height = getSubStringTextSize().height; >+ >+ if (cachedImage == null || shouldInvalidateCache()) { >+ invalidationRequired = false; >+ cleanImage(); >+ cachedImage = new Image(Display.getCurrent(), width, height); >+ >+ // @tag TODO : Dispose of the image properly >+ // ZestPlugin.getDefault().addImage(cachedImage.toString(), >+ // cachedImage); >+ >+ GC gc = new GC(cachedImage); >+ >+ Graphics graphics2 = new SWTGraphics(gc); >+ graphics2.setBackgroundColor(getBackgroundTextColor()); >+ graphics2.fillRectangle(0, 0, width, height); >+ graphics2.setForegroundColor(getForegroundColor()); >+ // graphics2.drawText(getSubStringText(), new Point(0, 0)); >+ graphics2.drawText(getText(), new Point(0, 0)); >+ gc.dispose(); >+ >+ } >+ graphics.drawImage(cachedImage, getTextLocation()); >+ graphics.translate(-bounds.x, -bounds.y); >+ this.paintBorder(graphics); >+ >+ } >+ >+ /** >+ * Determine if the image should be remade or not >+ * >+ * @return >+ */ >+ private boolean shouldInvalidateCache() { >+ if (invalidationRequired && !Animation.isAnimating()) { >+ return true; >+ } else { >+ return false; >+ } >+ } >+ >+ /** >+ * Notifies the cache that the image will need updating. >+ */ >+ private void updateInvalidation() { >+ invalidationRequired = true; >+ } >+ >+ protected void cleanImage() { >+ if (cachedImage != null) { >+ >+ // ZestPlugin.getDefault().removeImage(cachedImage.toString()); >+ cachedImage.dispose(); >+ cachedImage = null; >+ } >+ } >+} >Index: src/org/eclipse/zest/core/widgets/internal/ContainerFigure.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/widgets/internal/ContainerFigure.java,v >retrieving revision 1.1 >diff -u -r1.1 ContainerFigure.java >--- src/org/eclipse/zest/core/widgets/internal/ContainerFigure.java 30 Jan 2009 01:14:33 -0000 1.1 >+++ src/org/eclipse/zest/core/widgets/internal/ContainerFigure.java 15 Mar 2010 03:49:02 -0000 >@@ -1,18 +1,18 @@ >-/******************************************************************************* >- * Copyright (c) 2009 EclipseSource and others. 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: EclipseSource - initial API and implementation >- ******************************************************************************/ >-package org.eclipse.zest.core.widgets.internal; >- >-import org.eclipse.draw2d.Figure; >- >-/** >- * A figure used as the root of a container. >- */ >-public class ContainerFigure extends Figure { >- >-} >+/******************************************************************************* >+ * Copyright (c) 2009-2010 EclipseSource and others. 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: EclipseSource - initial API and implementation >+ ******************************************************************************/ >+package org.eclipse.zest.core.widgets.internal; >+ >+import org.eclipse.draw2d.Figure; >+ >+/** >+ * A figure used as the root of a container. >+ */ >+public class ContainerFigure extends Figure { >+ >+} >Index: src/org/eclipse/zest/core/widgets/internal/ExpandGraphLabel.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/widgets/internal/ExpandGraphLabel.java,v >retrieving revision 1.5 >diff -u -r1.5 ExpandGraphLabel.java >--- src/org/eclipse/zest/core/widgets/internal/ExpandGraphLabel.java 12 Sep 2007 20:44:39 -0000 1.5 >+++ src/org/eclipse/zest/core/widgets/internal/ExpandGraphLabel.java 15 Mar 2010 03:49:02 -0000 >@@ -1,283 +1,296 @@ >-/******************************************************************************* >- * Copyright 2005-2006, 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.widgets.internal; >- >-import org.eclipse.draw2d.ActionEvent; >-import org.eclipse.draw2d.ActionListener; >-import org.eclipse.draw2d.Clickable; >-import org.eclipse.draw2d.ColorConstants; >-import org.eclipse.draw2d.Figure; >-import org.eclipse.draw2d.FigureUtilities; >-import org.eclipse.draw2d.FreeformLayout; >-import org.eclipse.draw2d.Graphics; >-import org.eclipse.draw2d.Label; >-import org.eclipse.draw2d.ToolbarLayout; >-import org.eclipse.draw2d.Triangle; >-import org.eclipse.draw2d.geometry.Dimension; >-import org.eclipse.draw2d.geometry.Point; >-import org.eclipse.draw2d.geometry.Rectangle; >-import org.eclipse.zest.core.widgets.GraphContainer; >-import org.eclipse.swt.graphics.Color; >-import org.eclipse.swt.graphics.Font; >-import org.eclipse.swt.graphics.Image; >-import org.eclipse.swt.graphics.RGB; >-import org.eclipse.swt.widgets.Display; >- >-public class ExpandGraphLabel extends Figure implements ActionListener { >- >- public static final int OPEN = 1; >- public static final int CLOSED = 2; >- private int state = CLOSED; >- private Expander expander = null; >- >- class Expander extends Clickable { >- private Triangle triangle; >- >- public Expander() { >- setStyle(Clickable.STYLE_TOGGLE); >- triangle = new Triangle(); >- triangle.setSize(10, 10); >- triangle.setBackgroundColor(ColorConstants.black); >- triangle.setForegroundColor(ColorConstants.black); >- triangle.setFill(true); >- triangle.setDirection(Triangle.EAST); >- triangle.setLocation(new Point(5, 3)); >- this.setLayoutManager(new FreeformLayout()); >- this.add(triangle); >- this.setPreferredSize(15, 15); >- this.addActionListener(ExpandGraphLabel.this); >- } >- >- public void open() { >- triangle.setDirection(Triangle.SOUTH); >- } >- >- public void close() { >- triangle.setDirection(Triangle.EAST); >- } >- >- } >- >- /** >- * Sets the expander state (the little triangle) to ExpanderGraphLabel.OPEN or ExpanderGraphLabel.CLOSED >- * @param state >- */ >- public void setExpandedState(int state) { >- if (state == OPEN) { >- expander.open(); >- } else { >- expander.close(); >- } >- this.state = state; >- } >- >- /* >- * (non-Javadoc) >- * @see org.eclipse.draw2d.ActionListener#actionPerformed(org.eclipse.draw2d.ActionEvent) >- */ >- public void actionPerformed(ActionEvent event) { >- if (state == OPEN) { >- container.close(true); >- >- } else { >- container.open(true); >- } >- } >- >- private int arcWidth; >- private Label label = null; >- private final GraphContainer container; >- private ToolbarLayout layout; >- >- public ExpandGraphLabel(GraphContainer container, boolean cacheLabel) { >- this(container, "", null, cacheLabel); >- } >- >- public ExpandGraphLabel(GraphContainer container, Image i, boolean cacheLabel) { >- this(container, "", i, cacheLabel); >- } >- >- public ExpandGraphLabel(GraphContainer container, String text, boolean cacheLabel) { >- this(container, text, null, cacheLabel); >- } >- >- public ExpandGraphLabel(GraphContainer container, String text, Image image, boolean cacheLabel) { >- this.label = new Label(text) { >- >- /* >- * This method is overwritten so that the text is not truncated. >- * (non-Javadoc) >- * @see org.eclipse.draw2d.Label#paintFigure(org.eclipse.draw2d.Graphics) >- */ >- protected void paintFigure(Graphics graphics) { >- if (isOpaque()) { >- super.paintFigure(graphics); >- } >- Rectangle bounds = getBounds(); >- graphics.translate(bounds.x, bounds.y); >- if (getIcon() != null) { >- graphics.drawImage(getIcon(), getIconLocation()); >- } >- if (!isEnabled()) { >- graphics.translate(1, 1); >- graphics.setForegroundColor(ColorConstants.buttonLightest); >- graphics.drawText(getSubStringText(), getTextLocation()); >- graphics.translate(-1, -1); >- graphics.setForegroundColor(ColorConstants.buttonDarker); >- } >- graphics.drawText(getText(), getTextLocation()); >- graphics.translate(-bounds.x, -bounds.y); >- } >- }; >- this.setText(text); >- this.setImage(image); >- this.container = container; >- this.expander = new Expander(); >- this.arcWidth = 8; >- this.setFont(Display.getDefault().getSystemFont()); >- layout = new ToolbarLayout(true); >- layout.setSpacing(5); >- layout.setMinorAlignment(ToolbarLayout.ALIGN_CENTER); >- this.setLayoutManager(layout); >- this.add(this.expander); >- this.add(this.label); >- //this.remove(this.label); >- } >- >- /** >- * Adjust the bounds to make the text fit without truncation. >- */ >- protected void adjustBoundsToFit() { >- String text = getText(); >- if ((text != null) && (text.length() > 0)) { >- Font font = getFont(); >- if (font != null) { >- Dimension minSize = FigureUtilities.getTextExtents(text, font); >- if (getIcon() != null) { >- org.eclipse.swt.graphics.Rectangle imageRect = getIcon().getBounds(); >- int expandHeight = Math.max(imageRect.height - minSize.height, 0); >- minSize.expand(imageRect.width + 4, expandHeight); >- } >- minSize.expand(10 + (2 * 1) + 100, 4 + (2 * 1)); >- label.setBounds(new Rectangle(getLocation(), minSize)); >- } >- } >- } >- >- private Image getIcon() { >- return this.label.getIcon(); >- } >- >- private String getText() { >- return this.label.getText(); >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.draw2d.Label#paintFigure(org.eclipse.draw2d.Graphics) >- */ >- public void paint(Graphics graphics) { >- >- int blue = getBackgroundColor().getBlue(); >- blue = (int) (blue - (blue * 0.20)); >- blue = blue > 0 ? blue : 0; >- >- int red = getBackgroundColor().getRed(); >- red = (int) (red - (red * 0.20)); >- red = red > 0 ? red : 0; >- >- int green = getBackgroundColor().getGreen(); >- green = (int) (green - (green * 0.20)); >- green = green > 0 ? green : 0; >- >- Color lightenColor = new Color(Display.getCurrent(), new RGB(red, green, blue)); >- graphics.setForegroundColor(lightenColor); >- graphics.setBackgroundColor(getBackgroundColor()); >- >- graphics.pushState(); >- >- // fill in the background >- Rectangle bounds = getBounds().getCopy(); >- Rectangle r = bounds.getCopy(); >- //r.x += arcWidth / 2; >- r.y += arcWidth / 2; >- //r.width -= arcWidth; >- r.height -= arcWidth; >- >- Rectangle top = bounds.getCopy(); >- top.height /= 2; >- //graphics.setForegroundColor(lightenColor); >- //graphics.setBackgroundColor(lightenColor); >- graphics.setForegroundColor(getBackgroundColor()); >- graphics.setBackgroundColor(getBackgroundColor()); >- graphics.fillRoundRectangle(top, arcWidth, arcWidth); >- >- top.y = top.y + top.height; >- //graphics.setForegroundColor(getBackgroundColor()); >- //graphics.setBackgroundColor(getBackgroundColor()); >- graphics.setForegroundColor(lightenColor); >- graphics.setBackgroundColor(lightenColor); >- graphics.fillRoundRectangle(top, arcWidth, arcWidth); >- >- //graphics.setForegroundColor(lightenColor); >- //graphics.setBackgroundColor(getBackgroundColor()); >- graphics.setBackgroundColor(lightenColor); >- graphics.setForegroundColor(getBackgroundColor()); >- graphics.fillGradient(r, true); >- >- super.paint(graphics); >- graphics.popState(); >- graphics.setForegroundColor(lightenColor); >- graphics.setBackgroundColor(lightenColor); >- // paint the border >- bounds.setSize(bounds.width - 1, bounds.height - 1); >- graphics.drawRoundRectangle(bounds, arcWidth, arcWidth); >- lightenColor.dispose(); >- } >- >-// public Dimension getPreferredSize(int hint, int hint2) { >- // return this.label.getPreferredSize(); >- //} >- >- public void setTextT(String string) { >- this.setPreferredSize(null); >- this.label.setText(string); >- this.add(label); >- //System.out.println(this.label.getPreferredSize()); >- this.layout.layout(this); >- this.invalidate(); >- this.revalidate(); >- this.validate(); >- //this.remove(label); >- } >- >- public void setText(String string) { >- this.label.setText(string); >- //this.label.setPreferredSize(500, 30); >- //adjustBoundsToFit(); >- } >- >- public void setImage(Image image) { >- this.label.setIcon(image); >- //adjustBoundsToFit(); >- } >- >- public void setLocation(Point p) { >- // TODO Auto-generated method stub >- super.setLocation(p); >- } >- >- public void setBounds(Rectangle rect) { >- // TODO Auto-generated method stub >- super.setBounds(rect); >- } >- >-} >+/******************************************************************************* >+ * Copyright 2005-2010, 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.widgets.internal; >+ >+import org.eclipse.draw2d.ActionEvent; >+import org.eclipse.draw2d.ActionListener; >+import org.eclipse.draw2d.Clickable; >+import org.eclipse.draw2d.ColorConstants; >+import org.eclipse.draw2d.Figure; >+import org.eclipse.draw2d.FigureUtilities; >+import org.eclipse.draw2d.FreeformLayout; >+import org.eclipse.draw2d.Graphics; >+import org.eclipse.draw2d.Label; >+import org.eclipse.draw2d.ToolbarLayout; >+import org.eclipse.draw2d.Triangle; >+import org.eclipse.draw2d.geometry.Dimension; >+import org.eclipse.draw2d.geometry.Point; >+import org.eclipse.draw2d.geometry.Rectangle; >+import org.eclipse.swt.graphics.Color; >+import org.eclipse.swt.graphics.Font; >+import org.eclipse.swt.graphics.Image; >+import org.eclipse.swt.graphics.RGB; >+import org.eclipse.swt.widgets.Display; >+import org.eclipse.zest.core.widgets.GraphContainer; >+ >+public class ExpandGraphLabel extends Figure implements ActionListener { >+ >+ public static final int OPEN = 1; >+ public static final int CLOSED = 2; >+ private int state = CLOSED; >+ private Expander expander = null; >+ >+ class Expander extends Clickable { >+ private Triangle triangle; >+ >+ public Expander() { >+ setStyle(Clickable.STYLE_TOGGLE); >+ triangle = new Triangle(); >+ triangle.setSize(10, 10); >+ triangle.setBackgroundColor(ColorConstants.black); >+ triangle.setForegroundColor(ColorConstants.black); >+ triangle.setFill(true); >+ triangle.setDirection(Triangle.EAST); >+ triangle.setLocation(new Point(5, 3)); >+ this.setLayoutManager(new FreeformLayout()); >+ this.add(triangle); >+ this.setPreferredSize(15, 15); >+ this.addActionListener(ExpandGraphLabel.this); >+ } >+ >+ public void open() { >+ triangle.setDirection(Triangle.SOUTH); >+ } >+ >+ public void close() { >+ triangle.setDirection(Triangle.EAST); >+ } >+ >+ } >+ >+ /** >+ * Sets the expander state (the little triangle) to ExpanderGraphLabel.OPEN >+ * or ExpanderGraphLabel.CLOSED >+ * >+ * @param state >+ */ >+ public void setExpandedState(int state) { >+ if (state == OPEN) { >+ expander.open(); >+ } else { >+ expander.close(); >+ } >+ this.state = state; >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see >+ * org.eclipse.draw2d.ActionListener#actionPerformed(org.eclipse.draw2d. >+ * ActionEvent) >+ */ >+ public void actionPerformed(ActionEvent event) { >+ if (state == OPEN) { >+ container.close(true); >+ >+ } else { >+ container.open(true); >+ } >+ } >+ >+ private int arcWidth; >+ private Label label = null; >+ private final GraphContainer container; >+ private ToolbarLayout layout; >+ >+ public ExpandGraphLabel(GraphContainer container, boolean cacheLabel) { >+ this(container, "", null, cacheLabel); >+ } >+ >+ public ExpandGraphLabel(GraphContainer container, Image i, >+ boolean cacheLabel) { >+ this(container, "", i, cacheLabel); >+ } >+ >+ public ExpandGraphLabel(GraphContainer container, String text, >+ boolean cacheLabel) { >+ this(container, text, null, cacheLabel); >+ } >+ >+ public ExpandGraphLabel(GraphContainer container, String text, Image image, >+ boolean cacheLabel) { >+ this.label = new Label(text) { >+ >+ /* >+ * This method is overwritten so that the text is not truncated. >+ * (non-Javadoc) >+ * >+ * @see >+ * org.eclipse.draw2d.Label#paintFigure(org.eclipse.draw2d.Graphics) >+ */ >+ protected void paintFigure(Graphics graphics) { >+ if (isOpaque()) { >+ super.paintFigure(graphics); >+ } >+ Rectangle bounds = getBounds(); >+ graphics.translate(bounds.x, bounds.y); >+ if (getIcon() != null) { >+ graphics.drawImage(getIcon(), getIconLocation()); >+ } >+ if (!isEnabled()) { >+ graphics.translate(1, 1); >+ graphics.setForegroundColor(ColorConstants.buttonLightest); >+ graphics.drawText(getSubStringText(), getTextLocation()); >+ graphics.translate(-1, -1); >+ graphics.setForegroundColor(ColorConstants.buttonDarker); >+ } >+ graphics.drawText(getText(), getTextLocation()); >+ graphics.translate(-bounds.x, -bounds.y); >+ } >+ }; >+ this.setText(text); >+ this.setImage(image); >+ this.container = container; >+ this.expander = new Expander(); >+ this.arcWidth = 8; >+ this.setFont(Display.getDefault().getSystemFont()); >+ layout = new ToolbarLayout(true); >+ layout.setSpacing(5); >+ layout.setMinorAlignment(ToolbarLayout.ALIGN_CENTER); >+ this.setLayoutManager(layout); >+ this.add(this.expander); >+ this.add(this.label); >+ // this.remove(this.label); >+ } >+ >+ /** >+ * Adjust the bounds to make the text fit without truncation. >+ */ >+ protected void adjustBoundsToFit() { >+ String text = getText(); >+ if ((text != null) && (text.length() > 0)) { >+ Font font = getFont(); >+ if (font != null) { >+ Dimension minSize = FigureUtilities.getTextExtents(text, font); >+ if (getIcon() != null) { >+ org.eclipse.swt.graphics.Rectangle imageRect = getIcon() >+ .getBounds(); >+ int expandHeight = Math.max(imageRect.height >+ - minSize.height, 0); >+ minSize.expand(imageRect.width + 4, expandHeight); >+ } >+ minSize.expand(10 + (2 * 1) + 100, 4 + (2 * 1)); >+ label.setBounds(new Rectangle(getLocation(), minSize)); >+ } >+ } >+ } >+ >+ private Image getIcon() { >+ return this.label.getIcon(); >+ } >+ >+ private String getText() { >+ return this.label.getText(); >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see org.eclipse.draw2d.Label#paintFigure(org.eclipse.draw2d.Graphics) >+ */ >+ public void paint(Graphics graphics) { >+ >+ int blue = getBackgroundColor().getBlue(); >+ blue = (int) (blue - (blue * 0.20)); >+ blue = blue > 0 ? blue : 0; >+ >+ int red = getBackgroundColor().getRed(); >+ red = (int) (red - (red * 0.20)); >+ red = red > 0 ? red : 0; >+ >+ int green = getBackgroundColor().getGreen(); >+ green = (int) (green - (green * 0.20)); >+ green = green > 0 ? green : 0; >+ >+ Color lightenColor = new Color(Display.getCurrent(), new RGB(red, >+ green, blue)); >+ graphics.setForegroundColor(lightenColor); >+ graphics.setBackgroundColor(getBackgroundColor()); >+ >+ graphics.pushState(); >+ >+ // fill in the background >+ Rectangle bounds = getBounds().getCopy(); >+ Rectangle r = bounds.getCopy(); >+ // r.x += arcWidth / 2; >+ r.y += arcWidth / 2; >+ // r.width -= arcWidth; >+ r.height -= arcWidth; >+ >+ Rectangle top = bounds.getCopy(); >+ top.height /= 2; >+ // graphics.setForegroundColor(lightenColor); >+ // graphics.setBackgroundColor(lightenColor); >+ graphics.setForegroundColor(getBackgroundColor()); >+ graphics.setBackgroundColor(getBackgroundColor()); >+ graphics.fillRoundRectangle(top, arcWidth, arcWidth); >+ >+ top.y = top.y + top.height; >+ // graphics.setForegroundColor(getBackgroundColor()); >+ // graphics.setBackgroundColor(getBackgroundColor()); >+ graphics.setForegroundColor(lightenColor); >+ graphics.setBackgroundColor(lightenColor); >+ graphics.fillRoundRectangle(top, arcWidth, arcWidth); >+ >+ // graphics.setForegroundColor(lightenColor); >+ // graphics.setBackgroundColor(getBackgroundColor()); >+ graphics.setBackgroundColor(lightenColor); >+ graphics.setForegroundColor(getBackgroundColor()); >+ graphics.fillGradient(r, true); >+ >+ super.paint(graphics); >+ graphics.popState(); >+ graphics.setForegroundColor(lightenColor); >+ graphics.setBackgroundColor(lightenColor); >+ // paint the border >+ bounds.setSize(bounds.width - 1, bounds.height - 1); >+ graphics.drawRoundRectangle(bounds, arcWidth, arcWidth); >+ lightenColor.dispose(); >+ } >+ >+ // public Dimension getPreferredSize(int hint, int hint2) { >+ // return this.label.getPreferredSize(); >+ // } >+ >+ public void setTextT(String string) { >+ this.setPreferredSize(null); >+ this.label.setText(string); >+ this.add(label); >+ // System.out.println(this.label.getPreferredSize()); >+ this.layout.layout(this); >+ this.invalidate(); >+ this.revalidate(); >+ this.validate(); >+ // this.remove(label); >+ } >+ >+ public void setText(String string) { >+ this.label.setText(string); >+ // this.label.setPreferredSize(500, 30); >+ // adjustBoundsToFit(); >+ } >+ >+ public void setImage(Image image) { >+ this.label.setIcon(image); >+ // adjustBoundsToFit(); >+ } >+ >+ public void setLocation(Point p) { >+ // TODO Auto-generated method stub >+ super.setLocation(p); >+ } >+ >+ public void setBounds(Rectangle rect) { >+ // TODO Auto-generated method stub >+ super.setBounds(rect); >+ } >+ >+} >Index: src/org/eclipse/zest/core/widgets/internal/GraphLabel.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/widgets/internal/GraphLabel.java,v >retrieving revision 1.14 >diff -u -r1.14 GraphLabel.java >--- src/org/eclipse/zest/core/widgets/internal/GraphLabel.java 25 Apr 2009 06:08:58 -0000 1.14 >+++ src/org/eclipse/zest/core/widgets/internal/GraphLabel.java 15 Mar 2010 03:49:02 -0000 >@@ -1,286 +1,282 @@ >-/******************************************************************************* >- * Copyright 2005, 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.widgets.internal; >- >-import org.eclipse.draw2d.ColorConstants; >-import org.eclipse.draw2d.FigureUtilities; >-import org.eclipse.draw2d.Graphics; >-import org.eclipse.draw2d.MarginBorder; >-import org.eclipse.draw2d.ScaledGraphics; >-import org.eclipse.draw2d.StackLayout; >-import org.eclipse.draw2d.geometry.Dimension; >-import org.eclipse.draw2d.geometry.Rectangle; >-import org.eclipse.swt.graphics.Color; >-import org.eclipse.swt.graphics.Font; >-import org.eclipse.swt.graphics.Image; >-import org.eclipse.swt.graphics.RGB; >-import org.eclipse.swt.widgets.Display; >- >-/** >- * Overrides the Draw2D Label Figure class to ensure that the text is never >- * truncated. Also draws a rounded rectangle border. >- * >- * @author Chris Callendar >- */ >-public class GraphLabel extends CachedLabel { >- >- private Color borderColor; >- private int borderWidth; >- private int arcWidth; >- >- private boolean painting = false; >- >- /** >- * Creates a GraphLabel >- * >- * @param cacheLabel >- * Determine if the text should be cached. This will make it >- * faster, but the text is not as clear >- */ >- public GraphLabel(boolean cacheLabel) { >- this("", cacheLabel); >- } >- >- /** >- * Creates a graph label with text >- * >- * @param text >- * The text >- * @param cacheLabel >- * Determine if the text should be cached. This will make it >- * faster, but the >- */ >- public GraphLabel(String text, boolean cacheLabel) { >- this("", null, cacheLabel); >- } >- >- /** >- * Creates the graph label with an image >- * >- * @param i >- * The Image >- * @param cacheLabel >- * Determine if the text should be cached. This will make it >- * faster, but the >- */ >- public GraphLabel(Image i, boolean cacheLabel) { >- this("", i, cacheLabel); >- } >- >- /** >- * Creates a graph label with an image and text >- * >- * @param text >- * The text >- * @param i >- * The Image >- * @param cacheLabel >- * Determine if the text should be cached. This will make it >- * faster, but the >- */ >- public GraphLabel(String text, Image i, boolean cacheLabel) { >- super(cacheLabel); >- initLabel(); >- setText(text); >- setIcon(i); >- adjustBoundsToFit(); >- } >- >- /** >- * Initialises the border colour, border width, and sets the layout manager. >- * Also sets the font to be the default system font. >- */ >- protected void initLabel() { >- super.setFont(Display.getDefault().getSystemFont()); >- this.borderColor = ColorConstants.black; >- this.borderWidth = 0; >- this.arcWidth = 8; >- this.setLayoutManager(new StackLayout()); >- this.setBorder(new MarginBorder(1)); >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.draw2d.Figure#setFont(org.eclipse.swt.graphics.Font) >- */ >- public void setFont(Font f) { >- super.setFont(f); >- adjustBoundsToFit(); >- } >- >- /** >- * Adjust the bounds to make the text fit without truncation. >- */ >- protected void adjustBoundsToFit() { >- String text = getText(); >- int safeBorderWidth = borderWidth > 0 ? borderWidth : 1; >- if ((text != null)) { >- Font font = getFont(); >- if (font != null) { >- Dimension minSize = FigureUtilities.getTextExtents(text, font); >- if (getIcon() != null) { >- org.eclipse.swt.graphics.Rectangle imageRect = getIcon().getBounds(); >- int expandHeight = Math.max(imageRect.height - minSize.height, 0); >- minSize.expand(imageRect.width + 4, expandHeight); >- } >- minSize.expand(10 + (2 * safeBorderWidth), 4 + (2 * safeBorderWidth)); >- setBounds(new Rectangle(getLocation(), minSize)); >- } >- } >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.draw2d.Label#paintFigure(org.eclipse.draw2d.Graphics) >- */ >- public void paint(Graphics graphics) { >- int blue = getBackgroundColor().getBlue(); >- blue = (int) (blue - (blue * 0.20)); >- blue = blue > 0 ? blue : 0; >- >- int red = getBackgroundColor().getRed(); >- red = (int) (red - (red * 0.20)); >- red = red > 0 ? red : 0; >- >- int green = getBackgroundColor().getGreen(); >- green = (int) (green - (green * 0.20)); >- green = green > 0 ? green : 0; >- >- Color lightenColor = new Color(Display.getCurrent(), new RGB(red, green, blue)); >- graphics.setForegroundColor(lightenColor); >- graphics.setBackgroundColor(getBackgroundColor()); >- >- int safeBorderWidth = borderWidth > 0 ? borderWidth : 1; >- graphics.pushState(); >- double scale = 1; >- >- if (graphics instanceof ScaledGraphics) { >- scale = ((ScaledGraphics) graphics).getAbsoluteScale(); >- } >- // Top part inside the border (as fillGradient does not allow to fill a rectangle with round corners). >- Rectangle rect = getBounds().getCopy(); >- rect.height /= 2; >- graphics.setForegroundColor(getBackgroundColor()); >- graphics.setBackgroundColor(getBackgroundColor()); >- graphics.fillRoundRectangle(rect, arcWidth * safeBorderWidth, arcWidth * 2 * safeBorderWidth); >- >- // Bottom part inside the border. >- rect.y = rect.y + rect.height; >- rect.height += 1; // Not sure why it is needed, but it is needed ;-) >- graphics.setForegroundColor(lightenColor); >- graphics.setBackgroundColor(lightenColor); >- graphics.fillRoundRectangle(rect, arcWidth * safeBorderWidth, arcWidth * 2 * safeBorderWidth); >- >- // Now fill the middle part of top and bottom part with a gradient. >- rect = bounds.getCopy(); >- rect.height -= 2; >- rect.y += (safeBorderWidth) / 2; >- rect.y += (arcWidth / 2); >- rect.height -= arcWidth / 2; >- rect.height -= safeBorderWidth; >- graphics.setBackgroundColor(lightenColor); >- graphics.setForegroundColor(getBackgroundColor()); >- graphics.fillGradient(rect, true); >- >- // Paint the border >- if (borderWidth > 0) { >- rect = getBounds().getCopy(); >- rect.x += safeBorderWidth / 2; >- rect.y += safeBorderWidth / 2; >- rect.width -= safeBorderWidth; >- rect.height -= safeBorderWidth; >- graphics.setForegroundColor(borderColor); >- graphics.setBackgroundColor(borderColor); >- graphics.setLineWidth((int) (safeBorderWidth * scale)); >- graphics.drawRoundRectangle(rect, arcWidth, arcWidth); >- } >- >- super.paint(graphics); >- >- graphics.popState(); >- >- lightenColor.dispose(); >- } >- >- protected Color getBackgroundTextColor() { >- return getBackgroundColor(); >- } >- >- /** >- * This method is overridden to ensure that it doesn't get called while the >- * super.paintFigure() is being called. Otherwise NullPointerExceptions can >- * occur because the icon or text locations are cleared *after* they were >- * calculated. >- * >- * @see org.eclipse.draw2d.Label#invalidate() >- */ >- public void invalidate() { >- if (!painting) { >- super.invalidate(); >- } >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.draw2d.Label#setText(java.lang.String) >- */ >- public void setText(String s) { >- if (!s.equals("")) { >- super.setText(s); >- >- } else { >- super.setText(""); >- } >- adjustBoundsToFit(); >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.draw2d.Label#setIcon(org.eclipse.swt.graphics.Image) >- */ >- public void setIcon(Image image) { >- super.setIcon(image); >- //adjustBoundsToFit(); >- } >- >- public Color getBorderColor() { >- return borderColor; >- } >- >- public void setBorderColor(Color c) { >- this.borderColor = c; >- } >- >- public int getBorderWidth() { >- return borderWidth; >- } >- >- public void setBorderWidth(int width) { >- this.borderWidth = width; >- } >- >- public int getArcWidth() { >- return arcWidth; >- } >- >- public void setArcWidth(int arcWidth) { >- this.arcWidth = arcWidth; >- } >- >- public void setBounds(Rectangle rect) { >- // TODO Auto-generated method stub >- super.setBounds(rect); >- } >- >-} >+/******************************************************************************* >+ * Copyright 2009-2010, 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.widgets.internal; >+ >+import org.eclipse.draw2d.ColorConstants; >+import org.eclipse.draw2d.FigureUtilities; >+import org.eclipse.draw2d.Graphics; >+import org.eclipse.draw2d.MarginBorder; >+import org.eclipse.draw2d.ScaledGraphics; >+import org.eclipse.draw2d.StackLayout; >+import org.eclipse.draw2d.geometry.Dimension; >+import org.eclipse.draw2d.geometry.Rectangle; >+import org.eclipse.swt.graphics.Color; >+import org.eclipse.swt.graphics.Font; >+import org.eclipse.swt.graphics.Image; >+import org.eclipse.swt.graphics.RGB; >+import org.eclipse.swt.widgets.Display; >+ >+/** >+ * Overrides the Draw2D Label Figure class to ensure that the text is never >+ * truncated. Also draws a rounded rectangle border. >+ * >+ * @author Chris Callendar >+ */ >+public class GraphLabel extends CachedLabel { >+ >+ private Color borderColor; >+ private int borderWidth; >+ private int arcWidth; >+ >+ private boolean painting = false; >+ >+ /** >+ * Creates a GraphLabel >+ * >+ * @param cacheLabel >+ * Determine if the text should be cached. This will make it >+ * faster, but the text is not as clear >+ */ >+ public GraphLabel(boolean cacheLabel) { >+ this("", cacheLabel); >+ } >+ >+ /** >+ * Creates a graph label with text >+ * >+ * @param text >+ * The text >+ * @param cacheLabel >+ * Determine if the text should be cached. This will make it >+ * faster, but the >+ */ >+ public GraphLabel(String text, boolean cacheLabel) { >+ this(text, null, cacheLabel); >+ } >+ >+ /** >+ * Creates the graph label with an image >+ * >+ * @param i >+ * The Image >+ * @param cacheLabel >+ * Determine if the text should be cached. This will make it >+ * faster, but the >+ */ >+ public GraphLabel(Image i, boolean cacheLabel) { >+ this("", i, cacheLabel); >+ } >+ >+ /** >+ * Creates a graph label with an image and text >+ * >+ * @param text >+ * The text >+ * @param i >+ * The Image >+ * @param cacheLabel >+ * Determine if the text should be cached. This will make it >+ * faster, but the >+ */ >+ public GraphLabel(String text, Image i, boolean cacheLabel) { >+ super(cacheLabel); >+ initLabel(); >+ setText(text); >+ setIcon(i); >+ adjustBoundsToFit(); >+ } >+ >+ /** >+ * Initialises the border colour, border width, and sets the layout manager. >+ * Also sets the font to be the default system font. >+ */ >+ protected void initLabel() { >+ super.setFont(Display.getDefault().getSystemFont()); >+ this.borderColor = ColorConstants.black; >+ this.borderWidth = 0; >+ this.arcWidth = 8; >+ this.setLayoutManager(new StackLayout()); >+ this.setBorder(new MarginBorder(1)); >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see org.eclipse.draw2d.Figure#setFont(org.eclipse.swt.graphics.Font) >+ */ >+ public void setFont(Font f) { >+ super.setFont(f); >+ adjustBoundsToFit(); >+ } >+ >+ /** >+ * Adjust the bounds to make the text fit without truncation. >+ */ >+ protected void adjustBoundsToFit() { >+ String text = getText(); >+ int safeBorderWidth = borderWidth > 0 ? borderWidth : 1; >+ if ((text != null)) { >+ Font font = getFont(); >+ if (font != null) { >+ Dimension minSize = FigureUtilities.getTextExtents(text, font); >+ if (getIcon() != null) { >+ org.eclipse.swt.graphics.Rectangle imageRect = getIcon() >+ .getBounds(); >+ int expandHeight = Math.max(imageRect.height >+ - minSize.height, 0); >+ minSize.expand(imageRect.width + 4, expandHeight); >+ } >+ minSize.expand(10 + (2 * safeBorderWidth), >+ 4 + (2 * safeBorderWidth)); >+ setBounds(new Rectangle(getLocation(), minSize)); >+ } >+ } >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see org.eclipse.draw2d.Label#paintFigure(org.eclipse.draw2d.Graphics) >+ */ >+ public void paint(Graphics graphics) { >+ int blue = getBackgroundColor().getBlue(); >+ blue = (int) (blue - (blue * 0.20)); >+ blue = blue > 0 ? blue : 0; >+ >+ int red = getBackgroundColor().getRed(); >+ red = (int) (red - (red * 0.20)); >+ red = red > 0 ? red : 0; >+ >+ int green = getBackgroundColor().getGreen(); >+ green = (int) (green - (green * 0.20)); >+ green = green > 0 ? green : 0; >+ >+ Color lightenColor = new Color(Display.getCurrent(), new RGB(red, >+ green, blue)); >+ graphics.setForegroundColor(lightenColor); >+ graphics.setBackgroundColor(getBackgroundColor()); >+ >+ int safeBorderWidth = borderWidth > 0 ? borderWidth : 1; >+ graphics.pushState(); >+ double scale = 1; >+ >+ if (graphics instanceof ScaledGraphics) { >+ scale = ((ScaledGraphics) graphics).getAbsoluteScale(); >+ } >+ // Top part inside the border (as fillGradient does not allow to fill a >+ // rectangle with round corners). >+ Rectangle rect = getBounds().getCopy(); >+ rect.height /= 2; >+ graphics.setForegroundColor(getBackgroundColor()); >+ graphics.setBackgroundColor(getBackgroundColor()); >+ graphics.fillRoundRectangle(rect, arcWidth * safeBorderWidth, arcWidth >+ * 2 * safeBorderWidth); >+ >+ // Bottom part inside the border. >+ rect.y = rect.y + rect.height; >+ rect.height += 1; // Not sure why it is needed, but it is needed ;-) >+ graphics.setForegroundColor(lightenColor); >+ graphics.setBackgroundColor(lightenColor); >+ graphics.fillRoundRectangle(rect, arcWidth * safeBorderWidth, arcWidth >+ * 2 * safeBorderWidth); >+ >+ // Now fill the middle part of top and bottom part with a gradient. >+ rect = bounds.getCopy(); >+ rect.height -= 2; >+ rect.y += (safeBorderWidth) / 2; >+ rect.y += (arcWidth / 2); >+ rect.height -= arcWidth / 2; >+ rect.height -= safeBorderWidth; >+ graphics.setBackgroundColor(lightenColor); >+ graphics.setForegroundColor(getBackgroundColor()); >+ graphics.fillGradient(rect, true); >+ >+ // Paint the border >+ if (borderWidth > 0) { >+ rect = getBounds().getCopy(); >+ rect.x += safeBorderWidth / 2; >+ rect.y += safeBorderWidth / 2; >+ rect.width -= safeBorderWidth; >+ rect.height -= safeBorderWidth; >+ graphics.setForegroundColor(borderColor); >+ graphics.setBackgroundColor(borderColor); >+ graphics.setLineWidth((int) (safeBorderWidth * scale)); >+ graphics.drawRoundRectangle(rect, arcWidth, arcWidth); >+ } >+ >+ super.paint(graphics); >+ >+ graphics.popState(); >+ >+ lightenColor.dispose(); >+ } >+ >+ protected Color getBackgroundTextColor() { >+ return getBackgroundColor(); >+ } >+ >+ /** >+ * This method is overridden to ensure that it doesn't get called while the >+ * super.paintFigure() is being called. Otherwise NullPointerExceptions can >+ * occur because the icon or text locations are cleared *after* they were >+ * calculated. >+ * >+ * @see org.eclipse.draw2d.Label#invalidate() >+ */ >+ public void invalidate() { >+ if (!painting) { >+ super.invalidate(); >+ } >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see org.eclipse.draw2d.Label#setText(java.lang.String) >+ */ >+ public void setText(String s) { >+ super.setText(s); >+ adjustBoundsToFit(); >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see org.eclipse.draw2d.Label#setIcon(org.eclipse.swt.graphics.Image) >+ */ >+ public void setIcon(Image image) { >+ super.setIcon(image); >+ adjustBoundsToFit(); >+ } >+ >+ public Color getBorderColor() { >+ return borderColor; >+ } >+ >+ public void setBorderColor(Color c) { >+ this.borderColor = c; >+ } >+ >+ public int getBorderWidth() { >+ return borderWidth; >+ } >+ >+ public void setBorderWidth(int width) { >+ this.borderWidth = width; >+ } >+ >+ public int getArcWidth() { >+ return arcWidth; >+ } >+ >+ public void setArcWidth(int arcWidth) { >+ this.arcWidth = arcWidth; >+ } >+} >Index: src/org/eclipse/zest/core/widgets/internal/LoopAnchor.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/widgets/internal/LoopAnchor.java,v >retrieving revision 1.3 >diff -u -r1.3 LoopAnchor.java >--- src/org/eclipse/zest/core/widgets/internal/LoopAnchor.java 12 Sep 2007 20:44:39 -0000 1.3 >+++ src/org/eclipse/zest/core/widgets/internal/LoopAnchor.java 15 Mar 2010 03:49:02 -0000 >@@ -1,38 +1,40 @@ >-/******************************************************************************* >- * Copyright 2005-2006, 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.widgets.internal; >- >-import org.eclipse.draw2d.ChopboxAnchor; >-import org.eclipse.draw2d.IFigure; >-import org.eclipse.draw2d.geometry.Point; >- >-public class LoopAnchor extends ChopboxAnchor { >- public LoopAnchor(IFigure owner) { >- super(owner); >- } >- >- /* (non-Javadoc) >- * @see org.eclipse.draw2d.ChopboxAnchor#getReferencePoint() >- */ >- public Point getReferencePoint() { >- //modification to getReferencePoint. Returns >- //a point on the outside of the owners box, rather than the >- //center. Only usefull for self-loops. >- if (getOwner() == null) >- return null; >- else { >- Point ref = getOwner().getBounds().getCenter(); >- ref.y = getOwner().getBounds().y; >- getOwner().translateToAbsolute(ref); >- return ref; >- } >- } >-} >+/******************************************************************************* >+ * Copyright 2005-2010, 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.widgets.internal; >+ >+import org.eclipse.draw2d.ChopboxAnchor; >+import org.eclipse.draw2d.IFigure; >+import org.eclipse.draw2d.geometry.Point; >+ >+public class LoopAnchor extends ChopboxAnchor { >+ public LoopAnchor(IFigure owner) { >+ super(owner); >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see org.eclipse.draw2d.ChopboxAnchor#getReferencePoint() >+ */ >+ public Point getReferencePoint() { >+ // modification to getReferencePoint. Returns >+ // a point on the outside of the owners box, rather than the >+ // center. Only usefull for self-loops. >+ if (getOwner() == null) { >+ return null; >+ } else { >+ Point ref = getOwner().getBounds().getCenter(); >+ ref.y = getOwner().getBounds().y; >+ getOwner().translateToAbsolute(ref); >+ return ref; >+ } >+ } >+} >Index: src/org/eclipse/zest/core/widgets/internal/PolylineArcConnection.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/widgets/internal/PolylineArcConnection.java,v >retrieving revision 1.7 >diff -u -r1.7 PolylineArcConnection.java >--- src/org/eclipse/zest/core/widgets/internal/PolylineArcConnection.java 21 Nov 2008 17:02:38 -0000 1.7 >+++ src/org/eclipse/zest/core/widgets/internal/PolylineArcConnection.java 15 Mar 2010 03:49:02 -0000 >@@ -1,254 +1,260 @@ >-/******************************************************************************* >- * Copyright 2005-2006, 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.widgets.internal; >- >-import org.eclipse.draw2d.PolylineConnection; >-import org.eclipse.draw2d.RectangleFigure; >-import org.eclipse.draw2d.geometry.Point; >-import org.eclipse.draw2d.geometry.PointList; >- >-/* >- * A connection that draws an arc between nodes, based on a given depth for the >- * arc. This connection is drawn as an arc, defined as the circular arc with the >- * chord (ax, ay) - (bx, by) (where a and b are the anchors) and a depth d >- * defined as the maximum distance from any point on the chord (i.e. a vector >- * normal to the chord with magnitude d). >- * >- * @author Del Myers >- */ >-// @tag zest(bug(154391-ArcEnds(fix))) : force the endpoints to match by using a >-// polyline connection. >-// This will be more accurate than the regular ArcConnection, but it may be >-// slower. >-public class PolylineArcConnection extends PolylineConnection { >- private int depth; >- private boolean inverse = false; >- private static final float PI = (float) 3.14159; >- private RectangleFigure center; >- >- { >- this.depth = 0; >- center = new RectangleFigure(); >- } >- >- /* >- * (non-Javadoc) >- * >- * @see org.eclipse.draw2d.Polyline#setPoints(org.eclipse.draw2d.geometry.PointList) >- */ >- public void setPoints(PointList points) { >- updateArc(points); >- } >- >- /** >- * This method is not supported by this kind of connection. Points are >- * calculated based on the arc definition. >- */ >- public void addPoint(Point pt) { >- } >- >- /** >- * @param depth >- * the depth to set >- */ >- public void setDepth(int depth) { >- this.inverse = depth < 0 ? true : false; >- this.depth = depth; >- updateArc(getPoints()); >- } >- >- protected void updateArc(PointList pointList) { >- if (pointList.size() < 2) { >- return; >- } >- if (center.getParent() == this) { >- remove(center); >- } >- Point start = pointList.getFirstPoint(); >- Point end = pointList.getLastPoint(); >- if (depth == 0) { >- super.setPoints(pointList); >- return; >- } >- >- PointList points = new PointList(); >- >- float arcStart = 0; >- float arcEnd = 0; >- float arcLength = 0; >- float cartCenterX = 0; >- float cartCenterY = 0; >- float r = 0; >- >- float x1 = start.x; >- float y1 = -start.y; >- float x2 = end.x; >- float y2 = -end.y; >- float depth = this.depth; >- >- if (start.equals(end)) { >- // do a circle >- arcStart = -PI / 2; >- arcLength = PI * 2; >- // @tag drawing(arcs) : try making the center on a line from the >- // center of the parent figure. >- >- cartCenterX = x1; >- cartCenterY = y1 + depth / 2; >- r = depth / 2; >- >- } else { >- if (x1 >= x2) { >- depth = -depth; >- } >- // the center of the chord >- float cartChordX = (x2 + x1) / 2; >- float cartChordY = (y2 + y1) / 2; >- float chordLength = (float) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); >- if (Math.abs(depth) >= chordLength / 2) { >- depth = (chordLength / 3) * (depth / Math.abs(depth)); >- } >- r = ((((chordLength / 2) * (chordLength / 2)) + (depth * depth)) / (2 * depth)); >- >- // Find a vector normal to the chord. This will be used for >- // translating the >- // circle back to screen coordinates. >- float chordNormal = 0; >- >- if (Math.abs(x1 - x2) <= .000001) { >- // slope of 0. NaN is easier to detect than 0. >- chordNormal = Float.NaN; >- } else if (Math.abs(y1 - y2) <= 0.000001) { >- // infinite slope. >- chordNormal = Float.POSITIVE_INFINITY; >- } else { >- chordNormal = -1 * (y2 - y1) / (x2 - x1); >- } >- >- float th1; >- if (Float.isNaN(chordNormal)) { >- cartCenterX = (y1 > y2) ? (cartChordX - r + (depth)) : (cartChordX + r - (depth)); >- cartCenterY = cartChordY; >- th1 = PI / 2; >- } else if (Float.isInfinite(chordNormal)) { >- cartCenterX = cartChordX; >- cartCenterY = cartChordY + r - (depth); >- th1 = 0; >- } else { >- // assume that the center of the chord is on the origin. >- th1 = (float) Math.atan(chordNormal); >- cartCenterX = (r - (depth)) * (float) Math.sin(th1) + cartChordX;// cartChordX+r >- // -depth; >- cartCenterY = (r - (depth)) * (float) Math.cos(th1) + cartChordY;// cartChordY+r-depth; >- >- } >- // figure out the new angles >- // translate the points to the center of the circle >- float cartArcX1 = x1 - cartCenterX; >- float cartArcY1 = y1 - cartCenterY; >- float cartArcX2 = x2 - cartCenterX; >- float cartArcY2 = y2 - cartCenterY; >- >- // calculate the length of the arc >- arcStart = angleRadians(cartArcX1, cartArcY1); >- arcEnd = angleRadians(cartArcX2, cartArcY2); >- >- if (arcEnd < arcStart) { >- arcEnd = arcEnd + PI + PI; >- } >- >- // make sure that we are between the two nodes. >- arcLength = arcEnd - arcStart; >- float pad = PI / Math.abs(r); >- arcStart += pad; >- arcEnd -= pad; >- arcLength = (arcEnd) - (arcStart); >- if (inverse) { >- arcLength = (2 * PI - arcLength); >- } >- } >- // calculate the points >- r = Math.abs(r); >- float x = 0, y = 0; >- Point p = null; >- points.addPoint(start); >- float length = arcLength * r; >- >- int steps = (int) length / 16; >- if (steps < 10 && length > 10) { >- steps = 10; >- } >- if (arcLength < PI / 4 && steps > 6) { >- steps = 6; >- } >- if (steps < 4 && length > 4) { >- steps = 4; >- } >- float stepSize = arcLength / steps; >- if (inverse) { >- float step = arcStart - stepSize; >- for (int i = 1; i < steps; i++, step -= stepSize) { >- x = (r) * (float) Math.cos(step) + cartCenterX; >- y = (r) * (float) Math.sin(step) + cartCenterY; >- p = new Point(Math.round(x), Math.round(-y)); >- points.addPoint(p); >- } >- } else { >- float step = stepSize + arcStart; >- for (int i = 1; i < steps; i++, step += stepSize) { >- x = (r) * (float) Math.cos(step) + cartCenterX; >- y = (r) * (float) Math.sin(step) + cartCenterY; >- p = new Point(Math.round(x), Math.round(-y)); >- points.addPoint(p); >- } >- } >- points.addPoint(end); >- >- super.setPoints(points); >- } >- >- /* >- * Gets an angle in radians for the x, y coordinates. The angle will be >- * between 0 and 2PI. >- */ >- float angleRadians(float x, float y) { >- float theta = (float) Math.atan(y / x); >- switch (findQuadrant(x, y)) { >- case 1: >- return theta; >- case 2: >- return (theta + PI); >- case 4: >- theta = (theta + PI); >- case 3: >- return (theta + PI); >- default: >- return theta; >- } >- >- } >- >- // find the quadrant, assume points are centered at 0,0 >- protected int findQuadrant(float x, float y) { >- if (y > 0) { >- if (x > 0) { >- return 1; >- } else { >- return 2; >- } >- } else { >- if (x > 0) { >- return 4; >- } else { >- return 3; >- } >- } >- } >-} >+/******************************************************************************* >+ * Copyright 2005-2010, 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.widgets.internal; >+ >+import org.eclipse.draw2d.PolylineConnection; >+import org.eclipse.draw2d.RectangleFigure; >+import org.eclipse.draw2d.geometry.Point; >+import org.eclipse.draw2d.geometry.PointList; >+ >+/* >+ * A connection that draws an arc between nodes, based on a given depth for the >+ * arc. This connection is drawn as an arc, defined as the circular arc with the >+ * chord (ax, ay) - (bx, by) (where a and b are the anchors) and a depth d >+ * defined as the maximum distance from any point on the chord (i.e. a vector >+ * normal to the chord with magnitude d). >+ * >+ * @author Del Myers >+ */ >+// @tag zest(bug(154391-ArcEnds(fix))) : force the endpoints to match by using a >+// polyline connection. >+// This will be more accurate than the regular ArcConnection, but it may be >+// slower. >+public class PolylineArcConnection extends PolylineConnection { >+ private int depth; >+ private boolean inverse = false; >+ private static final float PI = (float) 3.14159; >+ private RectangleFigure center; >+ >+ { >+ this.depth = 0; >+ center = new RectangleFigure(); >+ } >+ >+ /* >+ * (non-Javadoc) >+ * >+ * @see >+ * org.eclipse.draw2d.Polyline#setPoints(org.eclipse.draw2d.geometry.PointList >+ * ) >+ */ >+ public void setPoints(PointList points) { >+ updateArc(points); >+ } >+ >+ /** >+ * This method is not supported by this kind of connection. Points are >+ * calculated based on the arc definition. >+ */ >+ public void addPoint(Point pt) { >+ } >+ >+ /** >+ * @param depth >+ * the depth to set >+ */ >+ public void setDepth(int depth) { >+ this.inverse = depth < 0 ? true : false; >+ this.depth = depth; >+ updateArc(getPoints()); >+ } >+ >+ protected void updateArc(PointList pointList) { >+ if (pointList.size() < 2) { >+ return; >+ } >+ if (center.getParent() == this) { >+ remove(center); >+ } >+ Point start = pointList.getFirstPoint(); >+ Point end = pointList.getLastPoint(); >+ if (depth == 0) { >+ super.setPoints(pointList); >+ return; >+ } >+ >+ PointList points = new PointList(); >+ >+ float arcStart = 0; >+ float arcEnd = 0; >+ float arcLength = 0; >+ float cartCenterX = 0; >+ float cartCenterY = 0; >+ float r = 0; >+ >+ float x1 = start.x; >+ float y1 = -start.y; >+ float x2 = end.x; >+ float y2 = -end.y; >+ float depth = this.depth; >+ >+ if (start.equals(end)) { >+ // do a circle >+ arcStart = -PI / 2; >+ arcLength = PI * 2; >+ // @tag drawing(arcs) : try making the center on a line from the >+ // center of the parent figure. >+ >+ cartCenterX = x1; >+ cartCenterY = y1 + depth / 2; >+ r = depth / 2; >+ >+ } else { >+ if (x1 >= x2) { >+ depth = -depth; >+ } >+ // the center of the chord >+ float cartChordX = (x2 + x1) / 2; >+ float cartChordY = (y2 + y1) / 2; >+ float chordLength = (float) Math.sqrt((x1 - x2) * (x1 - x2) >+ + (y1 - y2) * (y1 - y2)); >+ if (Math.abs(depth) >= chordLength / 2) { >+ depth = (chordLength / 3) * (depth / Math.abs(depth)); >+ } >+ r = ((((chordLength / 2) * (chordLength / 2)) + (depth * depth)) / (2 * depth)); >+ >+ // Find a vector normal to the chord. This will be used for >+ // translating the >+ // circle back to screen coordinates. >+ float chordNormal = 0; >+ >+ if (Math.abs(x1 - x2) <= .000001) { >+ // slope of 0. NaN is easier to detect than 0. >+ chordNormal = Float.NaN; >+ } else if (Math.abs(y1 - y2) <= 0.000001) { >+ // infinite slope. >+ chordNormal = Float.POSITIVE_INFINITY; >+ } else { >+ chordNormal = -1 * (y2 - y1) / (x2 - x1); >+ } >+ >+ float th1; >+ if (Float.isNaN(chordNormal)) { >+ cartCenterX = (y1 > y2) ? (cartChordX - r + (depth)) >+ : (cartChordX + r - (depth)); >+ cartCenterY = cartChordY; >+ th1 = PI / 2; >+ } else if (Float.isInfinite(chordNormal)) { >+ cartCenterX = cartChordX; >+ cartCenterY = cartChordY + r - (depth); >+ th1 = 0; >+ } else { >+ // assume that the center of the chord is on the origin. >+ th1 = (float) Math.atan(chordNormal); >+ cartCenterX = (r - (depth)) * (float) Math.sin(th1) >+ + cartChordX;// cartChordX+r >+ // -depth; >+ cartCenterY = (r - (depth)) * (float) Math.cos(th1) >+ + cartChordY;// cartChordY+r-depth; >+ >+ } >+ // figure out the new angles >+ // translate the points to the center of the circle >+ float cartArcX1 = x1 - cartCenterX; >+ float cartArcY1 = y1 - cartCenterY; >+ float cartArcX2 = x2 - cartCenterX; >+ float cartArcY2 = y2 - cartCenterY; >+ >+ // calculate the length of the arc >+ arcStart = angleRadians(cartArcX1, cartArcY1); >+ arcEnd = angleRadians(cartArcX2, cartArcY2); >+ >+ if (arcEnd < arcStart) { >+ arcEnd = arcEnd + PI + PI; >+ } >+ >+ // make sure that we are between the two nodes. >+ arcLength = arcEnd - arcStart; >+ float pad = PI / Math.abs(r); >+ arcStart += pad; >+ arcEnd -= pad; >+ arcLength = (arcEnd) - (arcStart); >+ if (inverse) { >+ arcLength = (2 * PI - arcLength); >+ } >+ } >+ // calculate the points >+ r = Math.abs(r); >+ float x = 0, y = 0; >+ Point p = null; >+ points.addPoint(start); >+ float length = arcLength * r; >+ >+ int steps = (int) length / 16; >+ if (steps < 10 && length > 10) { >+ steps = 10; >+ } >+ if (arcLength < PI / 4 && steps > 6) { >+ steps = 6; >+ } >+ if (steps < 4 && length > 4) { >+ steps = 4; >+ } >+ float stepSize = arcLength / steps; >+ if (inverse) { >+ float step = arcStart - stepSize; >+ for (int i = 1; i < steps; i++, step -= stepSize) { >+ x = (r) * (float) Math.cos(step) + cartCenterX; >+ y = (r) * (float) Math.sin(step) + cartCenterY; >+ p = new Point(Math.round(x), Math.round(-y)); >+ points.addPoint(p); >+ } >+ } else { >+ float step = stepSize + arcStart; >+ for (int i = 1; i < steps; i++, step += stepSize) { >+ x = (r) * (float) Math.cos(step) + cartCenterX; >+ y = (r) * (float) Math.sin(step) + cartCenterY; >+ p = new Point(Math.round(x), Math.round(-y)); >+ points.addPoint(p); >+ } >+ } >+ points.addPoint(end); >+ >+ super.setPoints(points); >+ } >+ >+ /* >+ * Gets an angle in radians for the x, y coordinates. The angle will be >+ * between 0 and 2PI. >+ */ >+ float angleRadians(float x, float y) { >+ float theta = (float) Math.atan(y / x); >+ switch (findQuadrant(x, y)) { >+ case 1: >+ return theta; >+ case 2: >+ return (theta + PI); >+ case 4: >+ theta = (theta + PI); >+ case 3: >+ return (theta + PI); >+ default: >+ return theta; >+ } >+ >+ } >+ >+ // find the quadrant, assume points are centered at 0,0 >+ protected int findQuadrant(float x, float y) { >+ if (y > 0) { >+ if (x > 0) { >+ return 1; >+ } else { >+ return 2; >+ } >+ } else { >+ if (x > 0) { >+ return 4; >+ } else { >+ return 3; >+ } >+ } >+ } >+} >Index: src/org/eclipse/zest/core/widgets/internal/RevealListener.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/widgets/internal/RevealListener.java,v >retrieving revision 1.3 >diff -u -r1.3 RevealListener.java >--- src/org/eclipse/zest/core/widgets/internal/RevealListener.java 12 Sep 2007 20:44:39 -0000 1.3 >+++ src/org/eclipse/zest/core/widgets/internal/RevealListener.java 15 Mar 2010 03:49:02 -0000 >@@ -1,25 +1,26 @@ >-/******************************************************************************* >- * Copyright 2005, 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.widgets.internal; >- >-import org.eclipse.swt.widgets.Control; >- >-/** >- * >- * A Listener to indicate when a view has become visible. >- * @author Ian Bull >- * >- */ >-public interface RevealListener { >- >- public void revealed( Control c ); >- >-} >+/******************************************************************************* >+ * Copyright 2005-2010, 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.widgets.internal; >+ >+import org.eclipse.swt.widgets.Control; >+ >+/** >+ * >+ * A Listener to indicate when a view has become visible. >+ * >+ * @author Ian Bull >+ * >+ */ >+public interface RevealListener { >+ >+ public void revealed(Control c); >+ >+} >Index: src/org/eclipse/zest/core/widgets/internal/RoundedChopboxAnchor.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/widgets/internal/RoundedChopboxAnchor.java,v >retrieving revision 1.3 >diff -u -r1.3 RoundedChopboxAnchor.java >--- src/org/eclipse/zest/core/widgets/internal/RoundedChopboxAnchor.java 12 Sep 2007 20:44:39 -0000 1.3 >+++ src/org/eclipse/zest/core/widgets/internal/RoundedChopboxAnchor.java 15 Mar 2010 03:49:02 -0000 >@@ -1,78 +1,88 @@ >-/******************************************************************************* >- * Copyright 2005, 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.widgets.internal; >- >-import org.eclipse.draw2d.ChopboxAnchor; >-import org.eclipse.draw2d.IFigure; >-import org.eclipse.draw2d.geometry.Point; >-import org.eclipse.draw2d.geometry.Rectangle; >- >-/** >- * A slight modification to the ChopboxAnchor class to account for the rounded corners. >- * >- * @author Chris Callendar >- */ >-public class RoundedChopboxAnchor extends ChopboxAnchor { >- >- private int arcRadius = 10; >- private int shift = 7; >- >- public RoundedChopboxAnchor(int arcRadius) { >- super(); >- this.arcRadius = arcRadius; >- this.shift = arcRadius - (int)(0.707 * arcRadius); >- } >- >- public RoundedChopboxAnchor(IFigure owner, int arcRadius) { >- super(owner); >- this.arcRadius = arcRadius; >- this.shift = arcRadius - (int)(0.707 * arcRadius); >- } >- >- /** >- * Modifies the point slightly for the rounded corners. >- * @return Point >- */ >- public Point getLocation(Point reference) { >- Point p = super.getLocation(reference); >- Rectangle bounds = getBox(); >- >- boolean done = getTranslatedPoint(bounds.getTopLeft(), p, shift, shift); >- if (!done) >- done = getTranslatedPoint(bounds.getTopRight(), p, -shift, shift); >- if (!done) >- done = getTranslatedPoint(bounds.getBottomLeft(), p, shift, -shift); >- if (!done) >- done = getTranslatedPoint(bounds.getBottomRight(), p, -shift, -shift); >- return p; >- } >- >- /** >- * Calculates the distance from the corner to the Point p. >- * If it is less than the minimum then it translates it and returns the new Point. >- * @param corner The corner Point. >- * @param p The point to translate if close to the corner. >- * @param dx The amount to translate in the x direcion. >- * @param dy The amount to translate in the y direcion. >- * @return boolean If the translation occured. >- */ >- private boolean getTranslatedPoint(Point corner, Point p, int dx, int dy) { >- int diff = (int)corner.getDistance(p); >- if (diff < arcRadius) { >- Point t = corner.getTranslated(dx, dy); >- p.setLocation(t.x, t.y); >- return true; >- } >- return false; >- } >- >- >-} >+/******************************************************************************* >+ * Copyright 2005-2010, 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.widgets.internal; >+ >+import org.eclipse.draw2d.ChopboxAnchor; >+import org.eclipse.draw2d.IFigure; >+import org.eclipse.draw2d.geometry.Point; >+import org.eclipse.draw2d.geometry.Rectangle; >+ >+/** >+ * A slight modification to the ChopboxAnchor class to account for the rounded >+ * corners. >+ * >+ * @author Chris Callendar >+ */ >+public class RoundedChopboxAnchor extends ChopboxAnchor { >+ >+ private int arcRadius = 10; >+ private int shift = 7; >+ >+ public RoundedChopboxAnchor(int arcRadius) { >+ super(); >+ this.arcRadius = arcRadius; >+ this.shift = arcRadius - (int) (0.707 * arcRadius); >+ } >+ >+ public RoundedChopboxAnchor(IFigure owner, int arcRadius) { >+ super(owner); >+ this.arcRadius = arcRadius; >+ this.shift = arcRadius - (int) (0.707 * arcRadius); >+ } >+ >+ /** >+ * Modifies the point slightly for the rounded corners. >+ * >+ * @return Point >+ */ >+ public Point getLocation(Point reference) { >+ Point p = super.getLocation(reference); >+ Rectangle bounds = getBox(); >+ >+ boolean done = getTranslatedPoint(bounds.getTopLeft(), p, shift, shift); >+ if (!done) { >+ done = getTranslatedPoint(bounds.getTopRight(), p, -shift, shift); >+ } >+ if (!done) { >+ done = getTranslatedPoint(bounds.getBottomLeft(), p, shift, -shift); >+ } >+ if (!done) { >+ done = getTranslatedPoint(bounds.getBottomRight(), p, -shift, >+ -shift); >+ } >+ return p; >+ } >+ >+ /** >+ * Calculates the distance from the corner to the Point p. If it is less >+ * than the minimum then it translates it and returns the new Point. >+ * >+ * @param corner >+ * The corner Point. >+ * @param p >+ * The point to translate if close to the corner. >+ * @param dx >+ * The amount to translate in the x direcion. >+ * @param dy >+ * The amount to translate in the y direcion. >+ * @return boolean If the translation occured. >+ */ >+ private boolean getTranslatedPoint(Point corner, Point p, int dx, int dy) { >+ int diff = (int) corner.getDistance(p); >+ if (diff < arcRadius) { >+ Point t = corner.getTranslated(dx, dy); >+ p.setLocation(t.x, t.y); >+ return true; >+ } >+ return false; >+ } >+ >+} >Index: src/org/eclipse/zest/core/widgets/internal/XYScaledGraphics.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/widgets/internal/XYScaledGraphics.java,v >retrieving revision 1.5 >diff -u -r1.5 XYScaledGraphics.java >--- src/org/eclipse/zest/core/widgets/internal/XYScaledGraphics.java 12 Sep 2007 20:44:39 -0000 1.5 >+++ src/org/eclipse/zest/core/widgets/internal/XYScaledGraphics.java 15 Mar 2010 03:49:02 -0000 >@@ -1,832 +1,870 @@ >-/******************************************************************************* >- * Copyright (c) 2000, 2005 IBM Corporation and others. 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: IBM Corporation - initial API and implementation Chisel Group, >- * University of Victoria - Adapted for XY Scaled Graphics >- ******************************************************************************/ >-package org.eclipse.zest.core.widgets.internal; >- >-import java.util.ArrayList; >-import java.util.HashMap; >-import java.util.Iterator; >-import java.util.List; >-import java.util.Map; >- >-import org.eclipse.draw2d.FigureUtilities; >-import org.eclipse.draw2d.Graphics; >-import org.eclipse.draw2d.ScaledGraphics; >-import org.eclipse.draw2d.geometry.Point; >-import org.eclipse.draw2d.geometry.PointList; >-import org.eclipse.draw2d.geometry.Rectangle; >-import org.eclipse.swt.graphics.Color; >-import org.eclipse.swt.graphics.Font; >-import org.eclipse.swt.graphics.FontData; >-import org.eclipse.swt.graphics.FontMetrics; >-import org.eclipse.swt.graphics.Image; >-import org.eclipse.swt.graphics.TextLayout; >-import org.eclipse.swt.graphics.TextStyle; >-import org.eclipse.swt.widgets.Display; >- >-/** >- * This was adapted from the ScaledGraphics class to allow X and Y to scale >- * independently. It won't require this level of coupling if some of these >- * private methods were made protected. I will open a bug report on this. >- * >- * @author irbull >- */ >-public class XYScaledGraphics extends ScaledGraphics { >- >- public static final double MAX_TEXT_SIZE = 0.45; // MAX size, when to stop zooming text >- >- private static class FontHeightCache { >- Font font; >- int height; >- } >- >- static class FontKey { >- Font font; >- int height; >- >- protected FontKey() { >- } >- >- protected FontKey(Font font, int height) { >- this.font = font; >- this.height = height; >- } >- >- public boolean equals(Object obj) { >- return (((FontKey) obj).font.equals(font) && ((FontKey) obj).height == height); >- } >- >- public int hashCode() { >- return font.hashCode() ^ height; >- } >- >- protected void setValues(Font font, int height) { >- this.font = font; >- this.height = height; >- } >- } >- >- /** >- * The internal state of the scaled graphics. >- */ >- protected static class State { >- private double appliedX; >- private double appliedY; >- private Font font; >- private int lineWidth; >- //private double zoom; // This has been replaced with xZoom and yZoom >- private double xZoom; >- private double yZoom; >- >- /** >- * Constructs a new, uninitialized State object. >- */ >- protected State() { >- } >- >- /** >- * Constructs a new State object and initializes the properties based on >- * the given values. >- * >- * @param zoom >- * the zoom factor >- * @param x >- * the x offset >- * @param y >- * the y offset >- * @param font >- * the font >- * @param lineWidth >- * the line width >- */ >- protected State(double xZoom, double yZoom, double x, double y, Font font, int lineWidth) { >- this.xZoom = xZoom; >- this.yZoom = yZoom; >- this.appliedX = x; >- this.appliedY = y; >- this.font = font; >- this.lineWidth = lineWidth; >- } >- >- /** >- * Sets all the properties of the state object. >- * >- * @param zoom >- * the zoom factor >- * @param x >- * the x offset >- * @param y >- * the y offset >- * @param font >- * the font >- * @param lineWidth >- * the line width >- */ >- protected void setValues(double xZoom, double yZoom, double x, double y, Font font, int lineWidth) { >- this.xZoom = xZoom; >- this.yZoom = yZoom; >- this.appliedX = x; >- this.appliedY = y; >- this.font = font; >- this.lineWidth = lineWidth; >- } >- } >- >- private static int[][] intArrayCache = new int[8][]; >- private final Rectangle tempRECT = new Rectangle(); >- >- static { >- for (int i = 0; i < intArrayCache.length; i++) { >- intArrayCache[i] = new int[i + 1]; >- } >- } >- >- private boolean allowText = true; >- // private static final Point PT = new Point(); >- private Map fontCache = new HashMap(); >- private Map fontDataCache = new HashMap(); >- private FontKey fontKey = new FontKey(); >- private double fractionalX; >- private double fractionalY; >- private Graphics graphics; >- private FontHeightCache localCache = new FontHeightCache(); >- private Font localFont; >- private int localLineWidth; >- private List stack = new ArrayList(); >- private int stackPointer = 0; >- private FontHeightCache targetCache = new FontHeightCache(); >- >- double xZoom = 1.0; >- double yZoom = 1.0; >- >- /** >- * Constructs a new ScaledGraphics based on the given Graphics object. >- * >- * @param g >- * the base graphics object >- */ >- public XYScaledGraphics(Graphics g) { >- super(g); >- graphics = g; >- localFont = g.getFont(); >- localLineWidth = g.getLineWidth(); >- } >- >- /** @see Graphics#clipRect(Rectangle) */ >- public void clipRect(Rectangle r) { >- graphics.clipRect(zoomClipRect(r)); >- } >- >- Font createFont(FontData data) { >- return new Font(Display.getCurrent(), data); >- } >- >- /** @see Graphics#dispose() */ >- public void dispose() { >- //Remove all states from the stack >- while (stackPointer > 0) { >- popState(); >- } >- >- //Dispose fonts >- Iterator iter = fontCache.values().iterator(); >- while (iter.hasNext()) { >- Font font = ((Font) iter.next()); >- font.dispose(); >- } >- >- } >- >- /** @see Graphics#drawArc(int, int, int, int, int, int) */ >- public void drawArc(int x, int y, int w, int h, int offset, int sweep) { >- Rectangle z = zoomRect(x, y, w, h); >- if (z.isEmpty() || sweep == 0) { >- return; >- } >- graphics.drawArc(z, offset, sweep); >- } >- >- /** @see Graphics#drawFocus(int, int, int, int) */ >- public void drawFocus(int x, int y, int w, int h) { >- graphics.drawFocus(zoomRect(x, y, w, h)); >- } >- >- /** @see Graphics#drawImage(Image, int, int) */ >- public void drawImage(Image srcImage, int x, int y) { >- org.eclipse.swt.graphics.Rectangle size = srcImage.getBounds(); >- double imageZoom = Math.min(xZoom, yZoom); >- graphics.drawImage(srcImage, 0, 0, size.width, size.height, (int) (Math.floor((x * xZoom + fractionalX))), (int) (Math.floor((y * yZoom + fractionalY))), (int) (Math.floor((size.width * imageZoom + fractionalX))), (int) (Math.floor((size.height * imageZoom + fractionalY)))); >- } >- >- /** @see Graphics#drawImage(Image, int, int, int, int, int, int, int, int) */ >- public void drawImage(Image srcImage, int sx, int sy, int sw, int sh, int tx, int ty, int tw, int th) { >- //"t" == target rectangle, "s" = source >- >- Rectangle t = zoomRect(tx, ty, tw, th); >- if (!t.isEmpty()) { >- graphics.drawImage(srcImage, sx, sy, sw, sh, t.x, t.y, t.width, t.height); >- } >- } >- >- /** @see Graphics#drawLine(int, int, int, int) */ >- public void drawLine(int x1, int y1, int x2, int y2) { >- graphics.drawLine((int) (Math.floor((x1 * xZoom + fractionalX))), (int) (Math.floor((y1 * yZoom + fractionalY))), (int) (Math.floor((x2 * xZoom + fractionalX))), (int) (Math.floor((y2 * yZoom + fractionalY)))); >- } >- >- /** @see Graphics#drawOval(int, int, int, int) */ >- public void drawOval(int x, int y, int w, int h) { >- graphics.drawOval(zoomRect(x, y, w, h)); >- } >- >- /** @see Graphics#drawPoint(int, int) */ >- public void drawPoint(int x, int y) { >- graphics.drawPoint((int) Math.floor(x * xZoom + fractionalX), (int) Math.floor(y * yZoom + fractionalY)); >- } >- >- /** >- * @see Graphics#drawPolygon(int[]) >- */ >- public void drawPolygon(int[] points) { >- graphics.drawPolygon(zoomPointList(points)); >- } >- >- /** @see Graphics#drawPolygon(PointList) */ >- public void drawPolygon(PointList points) { >- graphics.drawPolygon(zoomPointList(points.toIntArray())); >- } >- >- /** >- * @see Graphics#drawPolyline(int[]) >- */ >- public void drawPolyline(int[] points) { >- graphics.drawPolyline(zoomPointList(points)); >- } >- >- /** @see Graphics#drawPolyline(PointList) */ >- public void drawPolyline(PointList points) { >- graphics.drawPolyline(zoomPointList(points.toIntArray())); >- } >- >- /** @see Graphics#drawRectangle(int, int, int, int) */ >- public void drawRectangle(int x, int y, int w, int h) { >- graphics.drawRectangle(zoomRect(x, y, w, h)); >- } >- >- /** @see Graphics#drawRoundRectangle(Rectangle, int, int) */ >- public void drawRoundRectangle(Rectangle r, int arcWidth, int arcHeight) { >- graphics.drawRoundRectangle(zoomRect(r.x, r.y, r.width, r.height), (int) (arcWidth * xZoom), (int) (arcHeight * yZoom)); >- } >- >- /** @see Graphics#drawString(String, int, int) */ >- public void drawString(String s, int x, int y) { >- if (allowText) { >- graphics.drawString(s, zoomTextPoint(x, y)); >- } >- } >- >- /** @see Graphics#drawText(String, int, int) */ >- public void drawText(String s, int x, int y) { >- if (allowText) { >- graphics.drawText(s, zoomTextPoint(x, y)); >- } >- } >- >- /** >- * @see Graphics#drawText(String, int, int, int) >- */ >- public void drawText(String s, int x, int y, int style) { >- if (allowText) { >- graphics.drawText(s, zoomTextPoint(x, y), style); >- } >- } >- >- /** >- * @see Graphics#drawTextLayout(TextLayout, int, int, int, int, Color, >- * Color) >- */ >- public void drawTextLayout(TextLayout layout, int x, int y, int selectionStart, int selectionEnd, Color selectionForeground, Color selectionBackground) { >- TextLayout scaled = zoomTextLayout(layout); >- graphics.drawTextLayout(scaled, (int) Math.floor(x * xZoom + fractionalX), (int) Math.floor(y * yZoom + fractionalY), selectionStart, selectionEnd, selectionBackground, selectionForeground); >- scaled.dispose(); >- } >- >- /** @see Graphics#fillArc(int, int, int, int, int, int) */ >- public void fillArc(int x, int y, int w, int h, int offset, int sweep) { >- Rectangle z = zoomFillRect(x, y, w, h); >- if (z.isEmpty() || sweep == 0) { >- return; >- } >- graphics.fillArc(z, offset, sweep); >- } >- >- /** @see Graphics#fillGradient(int, int, int, int, boolean) */ >- public void fillGradient(int x, int y, int w, int h, boolean vertical) { >- graphics.fillGradient(zoomFillRect(x, y, w, h), vertical); >- } >- >- /** @see Graphics#fillOval(int, int, int, int) */ >- public void fillOval(int x, int y, int w, int h) { >- graphics.fillOval(zoomFillRect(x, y, w, h)); >- } >- >- /** >- * @see Graphics#fillPolygon(int[]) >- */ >- public void fillPolygon(int[] points) { >- graphics.fillPolygon(zoomPointList(points)); >- } >- >- /** @see Graphics#fillPolygon(PointList) */ >- public void fillPolygon(PointList points) { >- graphics.fillPolygon(zoomPointList(points.toIntArray())); >- } >- >- /** @see Graphics#fillRectangle(int, int, int, int) */ >- public void fillRectangle(int x, int y, int w, int h) { >- graphics.fillRectangle(zoomFillRect(x, y, w, h)); >- } >- >- /** @see Graphics#fillRoundRectangle(Rectangle, int, int) */ >- public void fillRoundRectangle(Rectangle r, int arcWidth, int arcHeight) { >- graphics.fillRoundRectangle(zoomFillRect(r.x, r.y, r.width, r.height), (int) (arcWidth * xZoom), (int) (arcHeight * yZoom)); >- } >- >- /** @see Graphics#fillString(String, int, int) */ >- public void fillString(String s, int x, int y) { >- if (allowText) { >- graphics.fillString(s, zoomTextPoint(x, y)); >- } >- } >- >- /** @see Graphics#fillText(String, int, int) */ >- public void fillText(String s, int x, int y) { >- if (allowText) { >- graphics.fillText(s, zoomTextPoint(x, y)); >- } >- } >- >- /** >- * @see Graphics#getAbsoluteScale() >- */ >- public double getAbsoluteScale() { >- return xZoom * graphics.getAbsoluteScale(); >- } >- >- /** >- * @see Graphics#getAlpha() >- */ >- public int getAlpha() { >- return graphics.getAlpha(); >- } >- >- /** >- * @see Graphics#getAntialias() >- */ >- public int getAntialias() { >- return graphics.getAntialias(); >- } >- >- /** @see Graphics#getBackgroundColor() */ >- public Color getBackgroundColor() { >- return graphics.getBackgroundColor(); >- } >- >- Font getCachedFont(FontKey key) { >- Font font = (Font) fontCache.get(key); >- if (font != null) { >- return font; >- } >- key = new FontKey(key.font, key.height); >- FontData data = key.font.getFontData()[0]; >- data.setHeight(key.height); >- Font zoomedFont = createFont(data); >- fontCache.put(key, zoomedFont); >- return zoomedFont; >- } >- >- FontData getCachedFontData(Font f) { >- FontData data = (FontData) fontDataCache.get(f); >- if (data != null) { >- return data; >- } >- data = getLocalFont().getFontData()[0]; >- fontDataCache.put(f, data); >- return data; >- } >- >- /** @see Graphics#getClip(Rectangle) */ >- public Rectangle getClip(Rectangle rect) { >- graphics.getClip(rect); >- int x = (int) (rect.x / xZoom); >- int y = (int) (rect.y / yZoom); >- /* >- * If the clip rectangle is queried, perform an inverse zoom, and take >- * the ceiling of the resulting double. This is necessary because >- * forward scaling essentially performs a floor() function. Without >- * this, figures will think that they don't need to paint when actually >- * they do. >- */ >- rect.width = (int) Math.ceil(rect.right() / xZoom) - x; >- rect.height = (int) Math.ceil(rect.bottom() / yZoom) - y; >- rect.x = x; >- rect.y = y; >- return rect; >- } >- >- /** >- * @see Graphics#getFillRule() >- */ >- public int getFillRule() { >- return graphics.getFillRule(); >- } >- >- /** @see Graphics#getFont() */ >- public Font getFont() { >- return getLocalFont(); >- } >- >- /** @see Graphics#getFontMetrics() */ >- public FontMetrics getFontMetrics() { >- return FigureUtilities.getFontMetrics(localFont); >- } >- >- /** @see Graphics#getForegroundColor() */ >- public Color getForegroundColor() { >- return graphics.getForegroundColor(); >- } >- >- /** >- * @see Graphics#getInterpolation() >- */ >- public int getInterpolation() { >- return graphics.getInterpolation(); >- } >- >- /** >- * @see Graphics#getLineCap() >- */ >- public int getLineCap() { >- return graphics.getLineCap(); >- } >- >- /** >- * @see Graphics#getLineJoin() >- */ >- public int getLineJoin() { >- return graphics.getLineJoin(); >- } >- >- /** @see Graphics#getLineStyle() */ >- public int getLineStyle() { >- return graphics.getLineStyle(); >- } >- >- /** @see Graphics#getLineWidth() */ >- public int getLineWidth() { >- return getLocalLineWidth(); >- } >- >- private Font getLocalFont() { >- return localFont; >- } >- >- private int getLocalLineWidth() { >- return localLineWidth; >- } >- >- /** >- * @see Graphics#getTextAntialias() >- */ >- public int getTextAntialias() { >- return graphics.getTextAntialias(); >- } >- >- /** @see Graphics#getXORMode() */ >- public boolean getXORMode() { >- return graphics.getXORMode(); >- } >- >- /** @see Graphics#popState() */ >- public void popState() { >- graphics.popState(); >- stackPointer--; >- restoreLocalState((State) stack.get(stackPointer)); >- } >- >- /** @see Graphics#pushState() */ >- public void pushState() { >- State s; >- if (stack.size() > stackPointer) { >- s = (State) stack.get(stackPointer); >- s.setValues(xZoom, yZoom, fractionalX, fractionalY, getLocalFont(), localLineWidth); >- } else { >- stack.add(new State(xZoom, yZoom, fractionalX, fractionalY, getLocalFont(), localLineWidth)); >- } >- stackPointer++; >- >- graphics.pushState(); >- } >- >- private void restoreLocalState(State state) { >- this.fractionalX = state.appliedX; >- this.fractionalY = state.appliedY; >- setScale(state.xZoom, state.yZoom); >- setLocalFont(state.font); >- setLocalLineWidth(state.lineWidth); >- } >- >- /** @see Graphics#restoreState() */ >- public void restoreState() { >- graphics.restoreState(); >- restoreLocalState((State) stack.get(stackPointer - 1)); >- } >- >- public void scale(double xAmount, double yAmount) { >- setScale(xZoom * xAmount, yZoom * yAmount); >- >- } >- >- /** @see Graphics#scale(double) */ >- public void scale(double amount) { >- //setScale(zoom * amount); >- throw new RuntimeException("Operation not supported, use scale(x, y)"); >- } >- >- /** >- * @see Graphics#setAlpha(int) >- */ >- public void setAlpha(int alpha) { >- graphics.setAlpha(alpha); >- } >- >- /** >- * @see Graphics#setAntialias(int) >- */ >- public void setAntialias(int value) { >- graphics.setAntialias(value); >- } >- >- /** @see Graphics#setBackgroundColor(Color) */ >- public void setBackgroundColor(Color rgb) { >- graphics.setBackgroundColor(rgb); >- } >- >- /** @see Graphics#setClip(Rectangle) */ >- public void setClip(Rectangle r) { >- graphics.setClip(zoomClipRect(r)); >- } >- >- /** >- * @see Graphics#setFillRule(int) >- */ >- public void setFillRule(int rule) { >- graphics.setFillRule(rule); >- } >- >- /** @see Graphics#setFont(Font) */ >- public void setFont(Font f) { >- setLocalFont(f); >- } >- >- /** @see Graphics#setForegroundColor(Color) */ >- public void setForegroundColor(Color rgb) { >- graphics.setForegroundColor(rgb); >- } >- >- /** >- * @see org.eclipse.draw2d.Graphics#setInterpolation(int) >- */ >- public void setInterpolation(int interpolation) { >- graphics.setInterpolation(interpolation); >- } >- >- /** >- * @see Graphics#setLineCap(int) >- */ >- public void setLineCap(int cap) { >- graphics.setLineCap(cap); >- } >- >- /** >- * @see Graphics#setLineDash(int[]) >- */ >- public void setLineDash(int[] dash) { >- graphics.setLineDash(dash); >- } >- >- /** >- * @see Graphics#setLineJoin(int) >- */ >- public void setLineJoin(int join) { >- graphics.setLineJoin(join); >- } >- >- /** @see Graphics#setLineStyle(int) */ >- public void setLineStyle(int style) { >- graphics.setLineStyle(style); >- } >- >- /** @see Graphics#setLineWidth(int) */ >- public void setLineWidth(int width) { >- setLocalLineWidth(width); >- } >- >- private void setLocalFont(Font f) { >- localFont = f; >- graphics.setFont(zoomFont(f)); >- } >- >- private void setLocalLineWidth(int width) { >- localLineWidth = width; >- graphics.setLineWidth(zoomLineWidth(width)); >- } >- >- public void setScale(double xValue, double yValue) { >- if (xValue == xZoom && yValue == yZoom) { >- return; >- } >- this.xZoom = xValue; >- this.yZoom = yValue; >- graphics.setFont(zoomFont(getLocalFont())); >- graphics.setLineWidth(zoomLineWidth(localLineWidth)); >- } >- >- void setScale(double value) { >- throw new RuntimeException("Operation not supported, use setScale(x,y)"); >- >- /* >- * if (zoom == value) return; this.zoom = value; >- * graphics.setFont(zoomFont(getLocalFont())); >- * graphics.setLineWidth(zoomLineWidth(localLineWidth)); >- */ >- } >- >- /** >- * @see Graphics#setTextAntialias(int) >- */ >- public void setTextAntialias(int value) { >- graphics.setTextAntialias(value); >- } >- >- /** @see Graphics#setXORMode(boolean) */ >- public void setXORMode(boolean b) { >- graphics.setXORMode(b); >- } >- >- /** @see Graphics#translate(int, int) */ >- public void translate(int dx, int dy) { >- // fractionalX/Y is the fractional part left over from previous >- // translates that gets lost in the integer approximation. >- double dxFloat = dx * xZoom + fractionalX; >- double dyFloat = dy * yZoom + fractionalY; >- fractionalX = dxFloat - Math.floor(dxFloat); >- fractionalY = dyFloat - Math.floor(dyFloat); >- graphics.translate((int) Math.floor(dxFloat), (int) Math.floor(dyFloat)); >- } >- >- private Rectangle zoomClipRect(Rectangle r) { >- tempRECT.x = (int) (Math.floor(r.x * xZoom + fractionalX)); >- tempRECT.y = (int) (Math.floor(r.y * yZoom + fractionalY)); >- tempRECT.width = (int) (Math.ceil(((r.x + r.width) * xZoom + fractionalX))) - tempRECT.x; >- tempRECT.height = (int) (Math.ceil(((r.y + r.height) * yZoom + fractionalY))) - tempRECT.y; >- return tempRECT; >- } >- >- private Rectangle zoomFillRect(int x, int y, int w, int h) { >- tempRECT.x = (int) (Math.floor((x * xZoom + fractionalX))); >- tempRECT.y = (int) (Math.floor((y * yZoom + fractionalY))); >- tempRECT.width = (int) (Math.floor(((x + w - 1) * xZoom + fractionalX))) - tempRECT.x + 1; >- tempRECT.height = (int) (Math.floor(((y + h - 1) * yZoom + fractionalY))) - tempRECT.y + 1; >- return tempRECT; >- } >- >- Font zoomFont(Font f) { >- if (f == null) { >- f = Display.getCurrent().getSystemFont(); >- } >- FontData data = getCachedFontData(f); >- int zoomedFontHeight = zoomFontHeight(data.getHeight()); >- allowText = zoomedFontHeight > 0; >- fontKey.setValues(f, zoomedFontHeight); >- return getCachedFont(fontKey); >- } >- >- int zoomFontHeight(int height) { >- double tmp = Math.min(yZoom, xZoom); >- if (tmp < MAX_TEXT_SIZE) { >- return (int) (tmp * height); >- } else { >- return (int) (height * tmp); >- } >- } >- >- int zoomLineWidth(int w) { >- return w; >- } >- >- private int[] zoomPointList(int[] points) { >- int[] scaled = null; >- >- // Look in cache for a integer array with the same length as 'points' >- for (int i = 0; i < intArrayCache.length; i++) { >- if (intArrayCache[i].length == points.length) { >- scaled = intArrayCache[i]; >- >- // Move this integer array up one notch in the array >- if (i != 0) { >- int[] temp = intArrayCache[i - 1]; >- intArrayCache[i - 1] = scaled; >- intArrayCache[i] = temp; >- } >- } >- } >- >- // If no match is found, take the one that is last and resize it. >- if (scaled == null) { >- intArrayCache[intArrayCache.length - 1] = new int[points.length]; >- scaled = intArrayCache[intArrayCache.length - 1]; >- } >- >- // Scale the points >- for (int i = 0; (i + 1) < points.length; i += 2) { >- scaled[i] = (int) (Math.floor((points[i] * xZoom + fractionalX))); >- scaled[i + 1] = (int) (Math.floor((points[i + 1] * yZoom + fractionalY))); >- } >- return scaled; >- } >- >- private Rectangle zoomRect(int x, int y, int w, int h) { >- tempRECT.x = (int) (Math.floor(x * xZoom + fractionalX)); >- tempRECT.y = (int) (Math.floor(y * yZoom + fractionalY)); >- tempRECT.width = (int) (Math.floor(((x + w) * xZoom + fractionalX))) - tempRECT.x; >- tempRECT.height = (int) (Math.floor(((y + h) * yZoom + fractionalY))) - tempRECT.y; >- return tempRECT; >- } >- >- private TextLayout zoomTextLayout(TextLayout layout) { >- TextLayout zoomed = new TextLayout(Display.getCurrent()); >- zoomed.setText(layout.getText()); >- >- int zoomWidth = -1; >- >- if (layout.getWidth() != -1) { >- zoomWidth = ((int) (layout.getWidth() * xZoom)); >- } >- >- if (zoomWidth < -1 || zoomWidth == 0) { >- return null; >- } >- >- zoomed.setFont(zoomFont(layout.getFont())); >- zoomed.setAlignment(layout.getAlignment()); >- zoomed.setAscent(layout.getAscent()); >- zoomed.setDescent(layout.getDescent()); >- zoomed.setOrientation(layout.getOrientation()); >- zoomed.setSegments(layout.getSegments()); >- zoomed.setSpacing(layout.getSpacing()); >- zoomed.setTabs(layout.getTabs()); >- >- zoomed.setWidth(zoomWidth); >- int length = layout.getText().length(); >- if (length > 0) { >- int start = 0, offset = 1; >- TextStyle style = null, lastStyle = layout.getStyle(0); >- for (; offset <= length; offset++) { >- if (offset != length && (style = layout.getStyle(offset)) == lastStyle) { >- continue; >- } >- int end = offset - 1; >- >- if (lastStyle != null) { >- TextStyle zoomedStyle = new TextStyle(zoomFont(lastStyle.font), lastStyle.foreground, lastStyle.background); >- zoomed.setStyle(zoomedStyle, start, end); >- } >- lastStyle = style; >- start = offset; >- } >- } >- return zoomed; >- } >- >- private Point zoomTextPoint(int x, int y) { >- if (localCache.font != localFont) { >- //Font is different, re-calculate its height >- FontMetrics metric = FigureUtilities.getFontMetrics(localFont); >- localCache.height = metric.getHeight() - metric.getDescent(); >- localCache.font = localFont; >- } >- if (targetCache.font != graphics.getFont()) { >- FontMetrics metric = graphics.getFontMetrics(); >- targetCache.font = graphics.getFont(); >- targetCache.height = metric.getHeight() - metric.getDescent(); >- } >- return new Point(((int) (Math.floor((x * xZoom) + fractionalX))), (int) (Math.floor((y + localCache.height - 1) * yZoom - targetCache.height + 1 + fractionalY))); >- } >- >-} >+/******************************************************************************* >+ * Copyright (c) 2005-2010, IBM Corporation and others. 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: IBM Corporation - initial API and implementation Chisel Group, >+ * University of Victoria - Adapted for XY Scaled Graphics >+ ******************************************************************************/ >+package org.eclipse.zest.core.widgets.internal; >+ >+import java.util.ArrayList; >+import java.util.HashMap; >+import java.util.Iterator; >+import java.util.List; >+import java.util.Map; >+ >+import org.eclipse.draw2d.FigureUtilities; >+import org.eclipse.draw2d.Graphics; >+import org.eclipse.draw2d.ScaledGraphics; >+import org.eclipse.draw2d.geometry.Point; >+import org.eclipse.draw2d.geometry.PointList; >+import org.eclipse.draw2d.geometry.Rectangle; >+import org.eclipse.swt.graphics.Color; >+import org.eclipse.swt.graphics.Font; >+import org.eclipse.swt.graphics.FontData; >+import org.eclipse.swt.graphics.FontMetrics; >+import org.eclipse.swt.graphics.Image; >+import org.eclipse.swt.graphics.TextLayout; >+import org.eclipse.swt.graphics.TextStyle; >+import org.eclipse.swt.widgets.Display; >+ >+/** >+ * This was adapted from the ScaledGraphics class to allow X and Y to scale >+ * independently. It won't require this level of coupling if some of these >+ * private methods were made protected. I will open a bug report on this. >+ * >+ * @author irbull >+ */ >+public class XYScaledGraphics extends ScaledGraphics { >+ >+ public static final double MAX_TEXT_SIZE = 0.45; // MAX size, when to stop >+ >+ // zooming text >+ >+ private static class FontHeightCache { >+ Font font; >+ int height; >+ } >+ >+ static class FontKey { >+ Font font; >+ int height; >+ >+ protected FontKey() { >+ } >+ >+ protected FontKey(Font font, int height) { >+ this.font = font; >+ this.height = height; >+ } >+ >+ public boolean equals(Object obj) { >+ return (((FontKey) obj).font.equals(font) && ((FontKey) obj).height == height); >+ } >+ >+ public int hashCode() { >+ return font.hashCode() ^ height; >+ } >+ >+ protected void setValues(Font font, int height) { >+ this.font = font; >+ this.height = height; >+ } >+ } >+ >+ /** >+ * The internal state of the scaled graphics. >+ */ >+ protected static class State { >+ private double appliedX; >+ private double appliedY; >+ private Font font; >+ private int lineWidth; >+ // private double zoom; // This has been replaced with xZoom and yZoom >+ private double xZoom; >+ private double yZoom; >+ >+ /** >+ * Constructs a new, uninitialized State object. >+ */ >+ protected State() { >+ } >+ >+ /** >+ * Constructs a new State object and initializes the properties based on >+ * the given values. >+ * >+ * @param zoom >+ * the zoom factor >+ * @param x >+ * the x offset >+ * @param y >+ * the y offset >+ * @param font >+ * the font >+ * @param lineWidth >+ * the line width >+ */ >+ protected State(double xZoom, double yZoom, double x, double y, >+ Font font, int lineWidth) { >+ this.xZoom = xZoom; >+ this.yZoom = yZoom; >+ this.appliedX = x; >+ this.appliedY = y; >+ this.font = font; >+ this.lineWidth = lineWidth; >+ } >+ >+ /** >+ * Sets all the properties of the state object. >+ * >+ * @param zoom >+ * the zoom factor >+ * @param x >+ * the x offset >+ * @param y >+ * the y offset >+ * @param font >+ * the font >+ * @param lineWidth >+ * the line width >+ */ >+ protected void setValues(double xZoom, double yZoom, double x, >+ double y, Font font, int lineWidth) { >+ this.xZoom = xZoom; >+ this.yZoom = yZoom; >+ this.appliedX = x; >+ this.appliedY = y; >+ this.font = font; >+ this.lineWidth = lineWidth; >+ } >+ } >+ >+ private static int[][] intArrayCache = new int[8][]; >+ private final Rectangle tempRECT = new Rectangle(); >+ >+ static { >+ for (int i = 0; i < intArrayCache.length; i++) { >+ intArrayCache[i] = new int[i + 1]; >+ } >+ } >+ >+ private boolean allowText = true; >+ // private static final Point PT = new Point(); >+ private Map fontCache = new HashMap(); >+ private Map fontDataCache = new HashMap(); >+ private FontKey fontKey = new FontKey(); >+ private double fractionalX; >+ private double fractionalY; >+ private Graphics graphics; >+ private FontHeightCache localCache = new FontHeightCache(); >+ private Font localFont; >+ private int localLineWidth; >+ private List stack = new ArrayList(); >+ private int stackPointer = 0; >+ private FontHeightCache targetCache = new FontHeightCache(); >+ >+ double xZoom = 1.0; >+ double yZoom = 1.0; >+ >+ /** >+ * Constructs a new ScaledGraphics based on the given Graphics object. >+ * >+ * @param g >+ * the base graphics object >+ */ >+ public XYScaledGraphics(Graphics g) { >+ super(g); >+ graphics = g; >+ localFont = g.getFont(); >+ localLineWidth = g.getLineWidth(); >+ } >+ >+ /** @see Graphics#clipRect(Rectangle) */ >+ public void clipRect(Rectangle r) { >+ graphics.clipRect(zoomClipRect(r)); >+ } >+ >+ Font createFont(FontData data) { >+ return new Font(Display.getCurrent(), data); >+ } >+ >+ /** @see Graphics#dispose() */ >+ public void dispose() { >+ // Remove all states from the stack >+ while (stackPointer > 0) { >+ popState(); >+ } >+ >+ // Dispose fonts >+ Iterator iter = fontCache.values().iterator(); >+ while (iter.hasNext()) { >+ Font font = ((Font) iter.next()); >+ font.dispose(); >+ } >+ >+ } >+ >+ /** @see Graphics#drawArc(int, int, int, int, int, int) */ >+ public void drawArc(int x, int y, int w, int h, int offset, int sweep) { >+ Rectangle z = zoomRect(x, y, w, h); >+ if (z.isEmpty() || sweep == 0) { >+ return; >+ } >+ graphics.drawArc(z, offset, sweep); >+ } >+ >+ /** @see Graphics#drawFocus(int, int, int, int) */ >+ public void drawFocus(int x, int y, int w, int h) { >+ graphics.drawFocus(zoomRect(x, y, w, h)); >+ } >+ >+ /** @see Graphics#drawImage(Image, int, int) */ >+ public void drawImage(Image srcImage, int x, int y) { >+ org.eclipse.swt.graphics.Rectangle size = srcImage.getBounds(); >+ double imageZoom = Math.min(xZoom, yZoom); >+ graphics.drawImage(srcImage, 0, 0, size.width, size.height, (int) (Math >+ .floor((x * xZoom + fractionalX))), (int) (Math.floor((y >+ * yZoom + fractionalY))), (int) (Math.floor((size.width >+ * imageZoom + fractionalX))), (int) (Math.floor((size.height >+ * imageZoom + fractionalY)))); >+ } >+ >+ /** @see Graphics#drawImage(Image, int, int, int, int, int, int, int, int) */ >+ public void drawImage(Image srcImage, int sx, int sy, int sw, int sh, >+ int tx, int ty, int tw, int th) { >+ // "t" == target rectangle, "s" = source >+ >+ Rectangle t = zoomRect(tx, ty, tw, th); >+ if (!t.isEmpty()) { >+ graphics.drawImage(srcImage, sx, sy, sw, sh, t.x, t.y, t.width, >+ t.height); >+ } >+ } >+ >+ /** @see Graphics#drawLine(int, int, int, int) */ >+ public void drawLine(int x1, int y1, int x2, int y2) { >+ graphics.drawLine((int) (Math.floor((x1 * xZoom + fractionalX))), >+ (int) (Math.floor((y1 * yZoom + fractionalY))), (int) (Math >+ .floor((x2 * xZoom + fractionalX))), (int) (Math >+ .floor((y2 * yZoom + fractionalY)))); >+ } >+ >+ /** @see Graphics#drawOval(int, int, int, int) */ >+ public void drawOval(int x, int y, int w, int h) { >+ graphics.drawOval(zoomRect(x, y, w, h)); >+ } >+ >+ /** @see Graphics#drawPoint(int, int) */ >+ public void drawPoint(int x, int y) { >+ graphics.drawPoint((int) Math.floor(x * xZoom + fractionalX), >+ (int) Math.floor(y * yZoom + fractionalY)); >+ } >+ >+ /** >+ * @see Graphics#drawPolygon(int[]) >+ */ >+ public void drawPolygon(int[] points) { >+ graphics.drawPolygon(zoomPointList(points)); >+ } >+ >+ /** @see Graphics#drawPolygon(PointList) */ >+ public void drawPolygon(PointList points) { >+ graphics.drawPolygon(zoomPointList(points.toIntArray())); >+ } >+ >+ /** >+ * @see Graphics#drawPolyline(int[]) >+ */ >+ public void drawPolyline(int[] points) { >+ graphics.drawPolyline(zoomPointList(points)); >+ } >+ >+ /** @see Graphics#drawPolyline(PointList) */ >+ public void drawPolyline(PointList points) { >+ graphics.drawPolyline(zoomPointList(points.toIntArray())); >+ } >+ >+ /** @see Graphics#drawRectangle(int, int, int, int) */ >+ public void drawRectangle(int x, int y, int w, int h) { >+ graphics.drawRectangle(zoomRect(x, y, w, h)); >+ } >+ >+ /** @see Graphics#drawRoundRectangle(Rectangle, int, int) */ >+ public void drawRoundRectangle(Rectangle r, int arcWidth, int arcHeight) { >+ graphics.drawRoundRectangle(zoomRect(r.x, r.y, r.width, r.height), >+ (int) (arcWidth * xZoom), (int) (arcHeight * yZoom)); >+ } >+ >+ /** @see Graphics#drawString(String, int, int) */ >+ public void drawString(String s, int x, int y) { >+ if (allowText) { >+ graphics.drawString(s, zoomTextPoint(x, y)); >+ } >+ } >+ >+ /** @see Graphics#drawText(String, int, int) */ >+ public void drawText(String s, int x, int y) { >+ if (allowText) { >+ graphics.drawText(s, zoomTextPoint(x, y)); >+ } >+ } >+ >+ /** >+ * @see Graphics#drawText(String, int, int, int) >+ */ >+ public void drawText(String s, int x, int y, int style) { >+ if (allowText) { >+ graphics.drawText(s, zoomTextPoint(x, y), style); >+ } >+ } >+ >+ /** >+ * @see Graphics#drawTextLayout(TextLayout, int, int, int, int, Color, >+ * Color) >+ */ >+ public void drawTextLayout(TextLayout layout, int x, int y, >+ int selectionStart, int selectionEnd, Color selectionForeground, >+ Color selectionBackground) { >+ TextLayout scaled = zoomTextLayout(layout); >+ graphics.drawTextLayout(scaled, (int) Math.floor(x * xZoom >+ + fractionalX), (int) Math.floor(y * yZoom + fractionalY), >+ selectionStart, selectionEnd, selectionBackground, >+ selectionForeground); >+ scaled.dispose(); >+ } >+ >+ /** @see Graphics#fillArc(int, int, int, int, int, int) */ >+ public void fillArc(int x, int y, int w, int h, int offset, int sweep) { >+ Rectangle z = zoomFillRect(x, y, w, h); >+ if (z.isEmpty() || sweep == 0) { >+ return; >+ } >+ graphics.fillArc(z, offset, sweep); >+ } >+ >+ /** @see Graphics#fillGradient(int, int, int, int, boolean) */ >+ public void fillGradient(int x, int y, int w, int h, boolean vertical) { >+ graphics.fillGradient(zoomFillRect(x, y, w, h), vertical); >+ } >+ >+ /** @see Graphics#fillOval(int, int, int, int) */ >+ public void fillOval(int x, int y, int w, int h) { >+ graphics.fillOval(zoomFillRect(x, y, w, h)); >+ } >+ >+ /** >+ * @see Graphics#fillPolygon(int[]) >+ */ >+ public void fillPolygon(int[] points) { >+ graphics.fillPolygon(zoomPointList(points)); >+ } >+ >+ /** @see Graphics#fillPolygon(PointList) */ >+ public void fillPolygon(PointList points) { >+ graphics.fillPolygon(zoomPointList(points.toIntArray())); >+ } >+ >+ /** @see Graphics#fillRectangle(int, int, int, int) */ >+ public void fillRectangle(int x, int y, int w, int h) { >+ graphics.fillRectangle(zoomFillRect(x, y, w, h)); >+ } >+ >+ /** @see Graphics#fillRoundRectangle(Rectangle, int, int) */ >+ public void fillRoundRectangle(Rectangle r, int arcWidth, int arcHeight) { >+ graphics.fillRoundRectangle(zoomFillRect(r.x, r.y, r.width, r.height), >+ (int) (arcWidth * xZoom), (int) (arcHeight * yZoom)); >+ } >+ >+ /** @see Graphics#fillString(String, int, int) */ >+ public void fillString(String s, int x, int y) { >+ if (allowText) { >+ graphics.fillString(s, zoomTextPoint(x, y)); >+ } >+ } >+ >+ /** @see Graphics#fillText(String, int, int) */ >+ public void fillText(String s, int x, int y) { >+ if (allowText) { >+ graphics.fillText(s, zoomTextPoint(x, y)); >+ } >+ } >+ >+ /** >+ * @see Graphics#getAbsoluteScale() >+ */ >+ public double getAbsoluteScale() { >+ return xZoom * graphics.getAbsoluteScale(); >+ } >+ >+ /** >+ * @see Graphics#getAlpha() >+ */ >+ public int getAlpha() { >+ return graphics.getAlpha(); >+ } >+ >+ /** >+ * @see Graphics#getAntialias() >+ */ >+ public int getAntialias() { >+ return graphics.getAntialias(); >+ } >+ >+ /** @see Graphics#getBackgroundColor() */ >+ public Color getBackgroundColor() { >+ return graphics.getBackgroundColor(); >+ } >+ >+ Font getCachedFont(FontKey key) { >+ Font font = (Font) fontCache.get(key); >+ if (font != null) { >+ return font; >+ } >+ key = new FontKey(key.font, key.height); >+ FontData data = key.font.getFontData()[0]; >+ data.setHeight(key.height); >+ Font zoomedFont = createFont(data); >+ fontCache.put(key, zoomedFont); >+ return zoomedFont; >+ } >+ >+ FontData getCachedFontData(Font f) { >+ FontData data = (FontData) fontDataCache.get(f); >+ if (data != null) { >+ return data; >+ } >+ data = getLocalFont().getFontData()[0]; >+ fontDataCache.put(f, data); >+ return data; >+ } >+ >+ /** @see Graphics#getClip(Rectangle) */ >+ public Rectangle getClip(Rectangle rect) { >+ graphics.getClip(rect); >+ int x = (int) (rect.x / xZoom); >+ int y = (int) (rect.y / yZoom); >+ /* >+ * If the clip rectangle is queried, perform an inverse zoom, and take >+ * the ceiling of the resulting double. This is necessary because >+ * forward scaling essentially performs a floor() function. Without >+ * this, figures will think that they don't need to paint when actually >+ * they do. >+ */ >+ rect.width = (int) Math.ceil(rect.right() / xZoom) - x; >+ rect.height = (int) Math.ceil(rect.bottom() / yZoom) - y; >+ rect.x = x; >+ rect.y = y; >+ return rect; >+ } >+ >+ /** >+ * @see Graphics#getFillRule() >+ */ >+ public int getFillRule() { >+ return graphics.getFillRule(); >+ } >+ >+ /** @see Graphics#getFont() */ >+ public Font getFont() { >+ return getLocalFont(); >+ } >+ >+ /** @see Graphics#getFontMetrics() */ >+ public FontMetrics getFontMetrics() { >+ return FigureUtilities.getFontMetrics(localFont); >+ } >+ >+ /** @see Graphics#getForegroundColor() */ >+ public Color getForegroundColor() { >+ return graphics.getForegroundColor(); >+ } >+ >+ /** >+ * @see Graphics#getInterpolation() >+ */ >+ public int getInterpolation() { >+ return graphics.getInterpolation(); >+ } >+ >+ /** >+ * @see Graphics#getLineCap() >+ */ >+ public int getLineCap() { >+ return graphics.getLineCap(); >+ } >+ >+ /** >+ * @see Graphics#getLineJoin() >+ */ >+ public int getLineJoin() { >+ return graphics.getLineJoin(); >+ } >+ >+ /** @see Graphics#getLineStyle() */ >+ public int getLineStyle() { >+ return graphics.getLineStyle(); >+ } >+ >+ /** @see Graphics#getLineWidth() */ >+ public int getLineWidth() { >+ return getLocalLineWidth(); >+ } >+ >+ private Font getLocalFont() { >+ return localFont; >+ } >+ >+ private int getLocalLineWidth() { >+ return localLineWidth; >+ } >+ >+ /** >+ * @see Graphics#getTextAntialias() >+ */ >+ public int getTextAntialias() { >+ return graphics.getTextAntialias(); >+ } >+ >+ /** @see Graphics#getXORMode() */ >+ public boolean getXORMode() { >+ return graphics.getXORMode(); >+ } >+ >+ /** @see Graphics#popState() */ >+ public void popState() { >+ graphics.popState(); >+ stackPointer--; >+ restoreLocalState((State) stack.get(stackPointer)); >+ } >+ >+ /** @see Graphics#pushState() */ >+ public void pushState() { >+ State s; >+ if (stack.size() > stackPointer) { >+ s = (State) stack.get(stackPointer); >+ s.setValues(xZoom, yZoom, fractionalX, fractionalY, getLocalFont(), >+ localLineWidth); >+ } else { >+ stack.add(new State(xZoom, yZoom, fractionalX, fractionalY, >+ getLocalFont(), localLineWidth)); >+ } >+ stackPointer++; >+ >+ graphics.pushState(); >+ } >+ >+ private void restoreLocalState(State state) { >+ this.fractionalX = state.appliedX; >+ this.fractionalY = state.appliedY; >+ setScale(state.xZoom, state.yZoom); >+ setLocalFont(state.font); >+ setLocalLineWidth(state.lineWidth); >+ } >+ >+ /** @see Graphics#restoreState() */ >+ public void restoreState() { >+ graphics.restoreState(); >+ restoreLocalState((State) stack.get(stackPointer - 1)); >+ } >+ >+ public void scale(double xAmount, double yAmount) { >+ setScale(xZoom * xAmount, yZoom * yAmount); >+ >+ } >+ >+ /** @see Graphics#scale(double) */ >+ public void scale(double amount) { >+ // setScale(zoom * amount); >+ throw new RuntimeException("Operation not supported, use scale(x, y)"); >+ } >+ >+ /** >+ * @see Graphics#setAlpha(int) >+ */ >+ public void setAlpha(int alpha) { >+ graphics.setAlpha(alpha); >+ } >+ >+ /** >+ * @see Graphics#setAntialias(int) >+ */ >+ public void setAntialias(int value) { >+ graphics.setAntialias(value); >+ } >+ >+ /** @see Graphics#setBackgroundColor(Color) */ >+ public void setBackgroundColor(Color rgb) { >+ graphics.setBackgroundColor(rgb); >+ } >+ >+ /** @see Graphics#setClip(Rectangle) */ >+ public void setClip(Rectangle r) { >+ graphics.setClip(zoomClipRect(r)); >+ } >+ >+ /** >+ * @see Graphics#setFillRule(int) >+ */ >+ public void setFillRule(int rule) { >+ graphics.setFillRule(rule); >+ } >+ >+ /** @see Graphics#setFont(Font) */ >+ public void setFont(Font f) { >+ setLocalFont(f); >+ } >+ >+ /** @see Graphics#setForegroundColor(Color) */ >+ public void setForegroundColor(Color rgb) { >+ graphics.setForegroundColor(rgb); >+ } >+ >+ /** >+ * @see org.eclipse.draw2d.Graphics#setInterpolation(int) >+ */ >+ public void setInterpolation(int interpolation) { >+ graphics.setInterpolation(interpolation); >+ } >+ >+ /** >+ * @see Graphics#setLineCap(int) >+ */ >+ public void setLineCap(int cap) { >+ graphics.setLineCap(cap); >+ } >+ >+ /** >+ * @see Graphics#setLineDash(int[]) >+ */ >+ public void setLineDash(int[] dash) { >+ graphics.setLineDash(dash); >+ } >+ >+ /** >+ * @see Graphics#setLineJoin(int) >+ */ >+ public void setLineJoin(int join) { >+ graphics.setLineJoin(join); >+ } >+ >+ /** @see Graphics#setLineStyle(int) */ >+ public void setLineStyle(int style) { >+ graphics.setLineStyle(style); >+ } >+ >+ /** @see Graphics#setLineWidth(int) */ >+ public void setLineWidth(int width) { >+ setLocalLineWidth(width); >+ } >+ >+ private void setLocalFont(Font f) { >+ localFont = f; >+ graphics.setFont(zoomFont(f)); >+ } >+ >+ private void setLocalLineWidth(int width) { >+ localLineWidth = width; >+ graphics.setLineWidth(zoomLineWidth(width)); >+ } >+ >+ public void setScale(double xValue, double yValue) { >+ if (xValue == xZoom && yValue == yZoom) { >+ return; >+ } >+ this.xZoom = xValue; >+ this.yZoom = yValue; >+ graphics.setFont(zoomFont(getLocalFont())); >+ graphics.setLineWidth(zoomLineWidth(localLineWidth)); >+ } >+ >+ void setScale(double value) { >+ throw new RuntimeException("Operation not supported, use setScale(x,y)"); >+ >+ /* >+ * if (zoom == value) return; this.zoom = value; >+ * graphics.setFont(zoomFont(getLocalFont())); >+ * graphics.setLineWidth(zoomLineWidth(localLineWidth)); >+ */ >+ } >+ >+ /** >+ * @see Graphics#setTextAntialias(int) >+ */ >+ public void setTextAntialias(int value) { >+ graphics.setTextAntialias(value); >+ } >+ >+ /** @see Graphics#setXORMode(boolean) */ >+ public void setXORMode(boolean b) { >+ graphics.setXORMode(b); >+ } >+ >+ /** @see Graphics#translate(int, int) */ >+ public void translate(int dx, int dy) { >+ // fractionalX/Y is the fractional part left over from previous >+ // translates that gets lost in the integer approximation. >+ double dxFloat = dx * xZoom + fractionalX; >+ double dyFloat = dy * yZoom + fractionalY; >+ fractionalX = dxFloat - Math.floor(dxFloat); >+ fractionalY = dyFloat - Math.floor(dyFloat); >+ graphics.translate((int) Math.floor(dxFloat), (int) Math.floor(dyFloat)); >+ } >+ >+ private Rectangle zoomClipRect(Rectangle r) { >+ tempRECT.x = (int) (Math.floor(r.x * xZoom + fractionalX)); >+ tempRECT.y = (int) (Math.floor(r.y * yZoom + fractionalY)); >+ tempRECT.width = (int) (Math >+ .ceil(((r.x + r.width) * xZoom + fractionalX))) >+ - tempRECT.x; >+ tempRECT.height = (int) (Math >+ .ceil(((r.y + r.height) * yZoom + fractionalY))) >+ - tempRECT.y; >+ return tempRECT; >+ } >+ >+ private Rectangle zoomFillRect(int x, int y, int w, int h) { >+ tempRECT.x = (int) (Math.floor((x * xZoom + fractionalX))); >+ tempRECT.y = (int) (Math.floor((y * yZoom + fractionalY))); >+ tempRECT.width = (int) (Math.floor(((x + w - 1) * xZoom + fractionalX))) >+ - tempRECT.x + 1; >+ tempRECT.height = (int) (Math >+ .floor(((y + h - 1) * yZoom + fractionalY))) >+ - tempRECT.y + 1; >+ return tempRECT; >+ } >+ >+ Font zoomFont(Font f) { >+ if (f == null) { >+ f = Display.getCurrent().getSystemFont(); >+ } >+ FontData data = getCachedFontData(f); >+ int zoomedFontHeight = zoomFontHeight(data.getHeight()); >+ allowText = zoomedFontHeight > 0; >+ fontKey.setValues(f, zoomedFontHeight); >+ return getCachedFont(fontKey); >+ } >+ >+ int zoomFontHeight(int height) { >+ double tmp = Math.min(yZoom, xZoom); >+ if (tmp < MAX_TEXT_SIZE) { >+ return (int) (tmp * height); >+ } else { >+ return (int) (height * tmp); >+ } >+ } >+ >+ int zoomLineWidth(int w) { >+ return w; >+ } >+ >+ private int[] zoomPointList(int[] points) { >+ int[] scaled = null; >+ >+ // Look in cache for a integer array with the same length as 'points' >+ for (int i = 0; i < intArrayCache.length; i++) { >+ if (intArrayCache[i].length == points.length) { >+ scaled = intArrayCache[i]; >+ >+ // Move this integer array up one notch in the array >+ if (i != 0) { >+ int[] temp = intArrayCache[i - 1]; >+ intArrayCache[i - 1] = scaled; >+ intArrayCache[i] = temp; >+ } >+ } >+ } >+ >+ // If no match is found, take the one that is last and resize it. >+ if (scaled == null) { >+ intArrayCache[intArrayCache.length - 1] = new int[points.length]; >+ scaled = intArrayCache[intArrayCache.length - 1]; >+ } >+ >+ // Scale the points >+ for (int i = 0; (i + 1) < points.length; i += 2) { >+ scaled[i] = (int) (Math.floor((points[i] * xZoom + fractionalX))); >+ scaled[i + 1] = (int) (Math >+ .floor((points[i + 1] * yZoom + fractionalY))); >+ } >+ return scaled; >+ } >+ >+ private Rectangle zoomRect(int x, int y, int w, int h) { >+ tempRECT.x = (int) (Math.floor(x * xZoom + fractionalX)); >+ tempRECT.y = (int) (Math.floor(y * yZoom + fractionalY)); >+ tempRECT.width = (int) (Math.floor(((x + w) * xZoom + fractionalX))) >+ - tempRECT.x; >+ tempRECT.height = (int) (Math.floor(((y + h) * yZoom + fractionalY))) >+ - tempRECT.y; >+ return tempRECT; >+ } >+ >+ private TextLayout zoomTextLayout(TextLayout layout) { >+ TextLayout zoomed = new TextLayout(Display.getCurrent()); >+ zoomed.setText(layout.getText()); >+ >+ int zoomWidth = -1; >+ >+ if (layout.getWidth() != -1) { >+ zoomWidth = ((int) (layout.getWidth() * xZoom)); >+ } >+ >+ if (zoomWidth < -1 || zoomWidth == 0) { >+ return null; >+ } >+ >+ zoomed.setFont(zoomFont(layout.getFont())); >+ zoomed.setAlignment(layout.getAlignment()); >+ zoomed.setAscent(layout.getAscent()); >+ zoomed.setDescent(layout.getDescent()); >+ zoomed.setOrientation(layout.getOrientation()); >+ zoomed.setSegments(layout.getSegments()); >+ zoomed.setSpacing(layout.getSpacing()); >+ zoomed.setTabs(layout.getTabs()); >+ >+ zoomed.setWidth(zoomWidth); >+ int length = layout.getText().length(); >+ if (length > 0) { >+ int start = 0, offset = 1; >+ TextStyle style = null, lastStyle = layout.getStyle(0); >+ for (; offset <= length; offset++) { >+ if (offset != length >+ && (style = layout.getStyle(offset)) == lastStyle) { >+ continue; >+ } >+ int end = offset - 1; >+ >+ if (lastStyle != null) { >+ TextStyle zoomedStyle = new TextStyle( >+ zoomFont(lastStyle.font), lastStyle.foreground, >+ lastStyle.background); >+ zoomed.setStyle(zoomedStyle, start, end); >+ } >+ lastStyle = style; >+ start = offset; >+ } >+ } >+ return zoomed; >+ } >+ >+ private Point zoomTextPoint(int x, int y) { >+ if (localCache.font != localFont) { >+ // Font is different, re-calculate its height >+ FontMetrics metric = FigureUtilities.getFontMetrics(localFont); >+ localCache.height = metric.getHeight() - metric.getDescent(); >+ localCache.font = localFont; >+ } >+ if (targetCache.font != graphics.getFont()) { >+ FontMetrics metric = graphics.getFontMetrics(); >+ targetCache.font = graphics.getFont(); >+ targetCache.height = metric.getHeight() - metric.getDescent(); >+ } >+ return new Point(((int) (Math.floor((x * xZoom) + fractionalX))), >+ (int) (Math.floor((y + localCache.height - 1) * yZoom >+ - targetCache.height + 1 + fractionalY))); >+ } >+ >+} >Index: src/org/eclipse/zest/core/widgets/internal/ZestRootLayer.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.core/src/org/eclipse/zest/core/widgets/internal/ZestRootLayer.java,v >retrieving revision 1.5 >diff -u -r1.5 ZestRootLayer.java >--- src/org/eclipse/zest/core/widgets/internal/ZestRootLayer.java 12 Sep 2007 20:44:39 -0000 1.5 >+++ src/org/eclipse/zest/core/widgets/internal/ZestRootLayer.java 15 Mar 2010 03:49:02 -0000 >@@ -1,189 +1,212 @@ >-/******************************************************************************* >- * Copyright (c) 2000, 2005 IBM Corporation and others. 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: IBM Corporation - initial API and implementation Chisel Group, >- * University of Victoria - Adapted for XY Scaled Graphics >- ******************************************************************************/ >-package org.eclipse.zest.core.widgets.internal; >- >-import org.eclipse.draw2d.FreeformLayer; >-import org.eclipse.draw2d.IFigure; >- >-/** >- * The root figure for Zest. The figure is broken up into four segments, >- * 1. The Connections >- * 2. The Nodes >- * 3. The Highlighted Connections >- * 4. The Highlighted Nodes >- * >- * @author Ian Bull >- * >- */ >-public class ZestRootLayer extends FreeformLayer { >- >- public static final boolean EDGES_ON_TOP = false; >- private int numberOfNodes = 0; >- private int numberOfConnections = 0; >- private int numberOfHighlightedNodes = 0; >- private int numberOfHighlightedConnections = 0; >- >- /** >- * Adds a node to the ZestRootLayer >- * @param nodeFigure The figure representing the node >- */ >- public void addNode(IFigure nodeFigure) { >- int nodePosition = getNodePosition(); >- numberOfNodes++; >- add(nodeFigure, nodePosition); >- } >- >- /** >- * Removes a node from the layer >- * @param nodeFigure >- */ >- public void removeNode(IFigure nodeFigure) { >- if (!this.getChildren().contains(nodeFigure)) { >- throw new RuntimeException("Node not contained on the ZestRootLayer"); >- } >- int nodePosition = this.getChildren().indexOf(nodeFigure); >- if (nodePosition > getHighlightNodeStartPosition()) { >- // The node is in the highlight node area >- numberOfHighlightedNodes--; >- } else { >- // The node is in the node area >- numberOfNodes--; >- } >- this.remove(nodeFigure); >- } >- >- public void removeConnection(IFigure connectionFigure) { >- int connectionPosition = this.getChildren().indexOf(connectionFigure); >- if (connectionPosition > getHighlightConnectionStartPosition()) { >- // The connection is in the highlight connection area >- numberOfHighlightedConnections--; >- } else { >- // The connection is in the connection area >- numberOfConnections--; >- } >- this.remove(connectionFigure); >- } >- >- public void addConnection(IFigure connectionFigure) { >- int connectionPosition = getConnectionPosition(); >- numberOfConnections++; >- add(connectionFigure, connectionPosition); >- } >- >- public void highlightNode(IFigure nodeFigure) { >- this.numberOfNodes--; >- int highlightNodePosition = getHighlightNodePosition(); >- this.numberOfHighlightedNodes++; >- this.getChildren().remove(nodeFigure); >- this.getChildren().add(highlightNodePosition, nodeFigure); >- this.invalidate(); >- this.repaint(); >- } >- >- public void highlightConnection(IFigure connectionFigure) { >- this.numberOfConnections--; >- int highlightConnectionPosition = getHighlightConnectionPosition(); >- this.numberOfHighlightedConnections++; >- this.getChildren().remove(connectionFigure); >- this.getChildren().add(highlightConnectionPosition, connectionFigure); >- this.invalidate(); >- this.repaint(); >- } >- >- public void unHighlightNode(IFigure nodeFigure) { >- int nodePosition = this.getChildren().indexOf(nodeFigure); >- if (nodePosition > getHighlightNodePosition()) { >- //throw new RuntimeException("Node: " + nodeFigure + " not currently Highlighted"); >- return; >- } >- this.numberOfHighlightedNodes--; >- nodePosition = getNodePosition(); >- this.numberOfNodes++; >- this.getChildren().remove(nodeFigure); >- this.getChildren().add(nodePosition, nodeFigure); >- this.invalidate(); >- this.repaint(); >- } >- >- public void unHighlightConnection(IFigure connectionFigure) { >- int connectionPosition = this.getChildren().indexOf(connectionFigure); >- if (connectionPosition > getHighlightConnectionPosition()) { >- //throw new RuntimeException("Connection: " + connectionFigure + " not currently Highlighted"); >- return; >- } >- this.numberOfHighlightedConnections--; >- this.numberOfConnections++; >- connectionPosition = getConnectionPosition(); >- this.getChildren().remove(connectionFigure); >- if (connectionPosition > this.getChildren().size()) { >- this.getChildren().add(connectionFigure); >- } else { >- this.getChildren().add(connectionPosition, connectionFigure); >- } >- this.invalidate(); >- this.repaint(); >- } >- >- /* >- * Node position is at the end of the list of nodes >- */ >- private int getNodePosition() { >- if (EDGES_ON_TOP) { >- return numberOfNodes; >- } >- return numberOfConnections + numberOfNodes; >- } >- >- /* >- * Connection position is at the end of the list of connections >- */ >- private int getConnectionPosition() { >- if (EDGES_ON_TOP) { >- return 0 + numberOfConnections + numberOfNodes; >- } >- return 0 + numberOfConnections; >- } >- >- /* >- * Highlight node position is at the end of the list of highlighted nodes >- */ >- private int getHighlightNodePosition() { >- if (EDGES_ON_TOP) { >- return numberOfConnections + numberOfNodes + numberOfHighlightedNodes; >- } >- return numberOfConnections + numberOfHighlightedConnections + numberOfNodes + numberOfHighlightedNodes; >- } >- >- /* >- * Highlighted connection position is at the end of the list of highlighted connections >- */ >- private int getHighlightConnectionPosition() { >- if (EDGES_ON_TOP) { >- return numberOfNodes + +numberOfConnections + numberOfHighlightedNodes + numberOfHighlightedConnections; >- } >- return numberOfConnections + numberOfNodes + numberOfHighlightedConnections; >- } >- >- private int getHighlightConnectionStartPosition() { >- if (EDGES_ON_TOP) { >- return numberOfConnections + numberOfNodes + numberOfHighlightedNodes; >- >- } >- return numberOfConnections + numberOfNodes; >- } >- >- private int getHighlightNodeStartPosition() { >- if (EDGES_ON_TOP) { >- return numberOfNodes + numberOfConnections; >- } >- return numberOfConnections + numberOfHighlightedConnections + numberOfNodes; >- } >- >-} >+/******************************************************************************* >+ * Copyright (c) 2005-2010 IBM Corporation and others. 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: IBM Corporation - initial API and implementation Chisel Group, >+ * University of Victoria - Adapted for XY Scaled Graphics >+ ******************************************************************************/ >+package org.eclipse.zest.core.widgets.internal; >+ >+import java.util.ArrayList; >+import java.util.HashSet; >+import java.util.Iterator; >+ >+import org.eclipse.draw2d.FreeformLayer; >+import org.eclipse.draw2d.IFigure; >+ >+/** >+ * The root figure for Zest. The figure is broken up into following segments: >+ * <ol> >+ * <li>The Connections</li> >+ * <li>The Subgraphs</li> >+ * <li>The Nodes</li> >+ * <li>The Highlighted Connections</li> >+ * <li>The Highlighted Nodes</li> >+ * </ol> >+ * >+ */ >+public class ZestRootLayer extends FreeformLayer { >+ >+ public static final int CONNECTIONS_LAYER = 0; >+ >+ public static final int SUBGRAPHS_LAYER = 1; >+ >+ public static final int NODES_LAYER = 2; >+ >+ public static final int CONNECTIONS_HIGHLIGHTED_LAYER = 3; >+ >+ public static final int NODES_HIGHLIGHTED_LAYER = 4; >+ >+ public static final int TOP_LAYER = 5; >+ >+ public static final int NUMBER_OF_LAYERS = 6; >+ >+ private final int[] itemsInLayer = new int[NUMBER_OF_LAYERS]; >+ >+ /** >+ * Set of all figures that are decorations for other figures. A decoration >+ * figure is always put one position (or more if there's more than one >+ * decoration for the same figure) after the decorated figure in children >+ * list. >+ */ >+ private HashSet decoratingFigures = new HashSet(); >+ >+ /** >+ * If true, it indicates that a figure is added using a proper method and >+ * its layer is known. Otherwise, Figure#add() method was used and layer >+ * must be guessed >+ */ >+ private boolean isLayerKnown = false; >+ >+ /** >+ * Adds a node to the ZestRootLayer >+ * >+ * @param nodeFigure >+ * The figure representing the node >+ */ >+ public void addNode(IFigure nodeFigure) { >+ addFigure(nodeFigure, NODES_LAYER); >+ } >+ >+ public void addConnection(IFigure connectionFigure) { >+ addFigure(connectionFigure, CONNECTIONS_LAYER); >+ } >+ >+ public void addSubgraph(IFigure subgraphFigrue) { >+ addFigure(subgraphFigrue, SUBGRAPHS_LAYER); >+ } >+ >+ public void highlightNode(IFigure nodeFigure) { >+ changeFigureLayer(nodeFigure, NODES_HIGHLIGHTED_LAYER); >+ } >+ >+ public void highlightConnection(IFigure connectionFigure) { >+ changeFigureLayer(connectionFigure, CONNECTIONS_HIGHLIGHTED_LAYER); >+ } >+ >+ public void unHighlightNode(IFigure nodeFigure) { >+ changeFigureLayer(nodeFigure, NODES_LAYER); >+ } >+ >+ public void unHighlightConnection(IFigure connectionFigure) { >+ changeFigureLayer(connectionFigure, CONNECTIONS_LAYER); >+ } >+ >+ private void changeFigureLayer(IFigure figure, int newLayer) { >+ ArrayList decorations = getDecorations(figure); >+ remove(figure); >+ >+ addFigure(figure, newLayer); >+ for (Iterator iterator = decorations.iterator(); iterator.hasNext();) { >+ addDecoration(figure, (IFigure) iterator.next()); >+ } >+ >+ this.invalidate(); >+ this.repaint(); >+ } >+ >+ private ArrayList getDecorations(IFigure figure) { >+ ArrayList result = new ArrayList(); >+ int index = getChildren().indexOf(figure); >+ if (index == -1) { >+ return result; >+ } >+ for (index++; index < getChildren().size(); index++) { >+ Object nextFigure = getChildren().get(index); >+ if (decoratingFigures.contains(nextFigure)) { >+ result.add(nextFigure); >+ } else { >+ break; >+ } >+ } >+ return result; >+ } >+ >+ /** >+ * >+ * @param layer >+ * @return position after the last element in given layer >+ */ >+ private int getPosition(int layer) { >+ int result = 0; >+ for (int i = 0; i <= layer; i++) { >+ result += itemsInLayer[i]; >+ } >+ return result; >+ } >+ >+ /** >+ * >+ * @param position >+ * @return number of layer containing element at given position >+ */ >+ private int getLayer(int position) { >+ int layer = 0; >+ int positionInLayer = itemsInLayer[0]; >+ while (layer < NUMBER_OF_LAYERS - 1 && positionInLayer <= position) { >+ layer++; >+ positionInLayer += itemsInLayer[layer]; >+ } >+ return layer; >+ } >+ >+ public void addFigure(IFigure figure, int layer) { >+ int position = getPosition(layer); >+ itemsInLayer[layer]++; >+ isLayerKnown = true; >+ add(figure, position); >+ } >+ >+ public void add(IFigure child, Object constraint, int index) { >+ super.add(child, constraint, index); >+ if (!isLayerKnown) { >+ int layer = 0, positionInLayer = itemsInLayer[0]; >+ while (positionInLayer < index) { >+ layer++; >+ positionInLayer += itemsInLayer[layer]; >+ } >+ if (index == -1) { >+ layer = NUMBER_OF_LAYERS - 1; >+ } >+ itemsInLayer[layer]++; >+ } >+ isLayerKnown = false; >+ } >+ >+ public void remove(IFigure child) { >+ int position = this.getChildren().indexOf(child); >+ if (position == -1) { >+ throw new RuntimeException( >+ "Can't remove a figure that is not on this ZestRootLayer"); >+ } >+ itemsInLayer[getLayer(position)]--; >+ if (decoratingFigures.contains(child)) { >+ decoratingFigures.remove(child); >+ super.remove(child); >+ } else { >+ ArrayList decorations = getDecorations(child); >+ super.remove(child); >+ for (Iterator iterator = decorations.iterator(); iterator.hasNext();) { >+ remove((IFigure) iterator.next()); >+ } >+ } >+ } >+ >+ public void addDecoration(IFigure decorated, IFigure decorating) { >+ int position = this.getChildren().indexOf(decorated); >+ if (position == -1) { >+ throw new RuntimeException( >+ "Can't add decoration for a figuer that is not on this ZestRootLayer"); >+ } >+ itemsInLayer[getLayer(position)]++; >+ isLayerKnown = true; >+ do { >+ position++; >+ } while (position < getChildren().size() >+ && decoratingFigures.contains(getChildren().get(position))); >+ decoratingFigures.add(decorating); >+ add(decorating, position); >+ } >+} >#P org.eclipse.zest.layouts >Index: META-INF/MANIFEST.MF >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.layouts/META-INF/MANIFEST.MF,v >retrieving revision 1.14 >diff -u -r1.14 MANIFEST.MF >--- META-INF/MANIFEST.MF 23 Apr 2009 03:41:00 -0000 1.14 >+++ META-INF/MANIFEST.MF 15 Mar 2010 03:49:04 -0000 >@@ -7,12 +7,8 @@ > Bundle-Localization: plugin > Export-Package: org.eclipse.zest.layouts, > org.eclipse.zest.layouts.algorithms, >- org.eclipse.zest.layouts.algorithms.internal, >- org.eclipse.zest.layouts.constraints, > org.eclipse.zest.layouts.dataStructures, >- org.eclipse.zest.layouts.exampleStructures, >- org.eclipse.zest.layouts.exampleUses, >- org.eclipse.zest.layouts.progress >+ org.eclipse.zest.layouts.interfaces > Require-Bundle: org.eclipse.swt;visibility:=reexport, > org.eclipse.core.runtime, > org.eclipse.jface, >Index: src/org/eclipse/zest/layouts/Filter.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/Filter.java >diff -N src/org/eclipse/zest/layouts/Filter.java >--- src/org/eclipse/zest/layouts/Filter.java 12 Jan 2008 01:43:30 -0000 1.4 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,30 +0,0 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts; >- >-/** >- * A filter is used to filter objects. Once implemented, interested >- * parties can ask this filter whether or not a specific object >- * is filtered. >- * >- * For example, in a visualization tool, only unfiltered objects should >- * be displayed. Before displaying an object, the display can ask >- * this filter if the object is filtered. >- * >- * @author Casey Best >- */ >-public interface Filter { >- >- /** >- * Returns true if the object is filtered, or false if it's not filtered. >- */ >- public boolean isObjectFiltered (LayoutItem object); >-} >Index: src/org/eclipse/zest/layouts/InvalidLayoutConfiguration.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/InvalidLayoutConfiguration.java >diff -N src/org/eclipse/zest/layouts/InvalidLayoutConfiguration.java >--- src/org/eclipse/zest/layouts/InvalidLayoutConfiguration.java 12 Sep 2007 20:44:37 -0000 1.4 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,23 +0,0 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts; >- >-/** >- * >- * @author Ian Bull >- * >- */ >-public class InvalidLayoutConfiguration extends Exception { >- >- static final long serialVersionUID = 0; >- >- >-} >Index: src/org/eclipse/zest/layouts/LayoutAlgorithm.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.layouts/src/org/eclipse/zest/layouts/LayoutAlgorithm.java,v >retrieving revision 1.7 >diff -u -r1.7 LayoutAlgorithm.java >--- src/org/eclipse/zest/layouts/LayoutAlgorithm.java 12 Sep 2007 20:44:37 -0000 1.7 >+++ src/org/eclipse/zest/layouts/LayoutAlgorithm.java 15 Mar 2010 03:49:04 -0000 >@@ -1,111 +1,50 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts; >- >-import java.util.Comparator; >-import java.util.List; >- >-import org.eclipse.zest.layouts.progress.ProgressListener; >- >-/** >- * A simple interface used by all layouts. >- * >- * Each layout Algorithm must implement the applyLayoutInternal method which actually compute the layout >- * >- * @author Casey Best >- * @author Ian Bull >- */ >-public interface LayoutAlgorithm { >- >- /** >- * Apply the layout to the given entities. The entities will be moved and resized based >- * on the algorithm. >- * >- * @param entitiesToLayout Apply the algorithm to these entities >- * @param relationshipsToConsider Only consider these relationships when applying the algorithm. >- * @param x The left side of the bounds in which the layout can place the entities. >- * @param y The top side of the bounds in which the layout can place the entities. >- * @param width The width of the bounds in which the layout can place the entities. >- * @param height The height of the bounds in which the layout can place the entities. >- * @param asynchronous Should the algorithm run Asynchronously >- */ >- public void applyLayout(LayoutEntity[] entitiesToLayout, LayoutRelationship[] relationshipsToConsider, double x, double y, double width, double height, boolean asynchronous, boolean continuous) throws InvalidLayoutConfiguration; >- >- /** >- * Returns whether or not the algorithm is currenly running >- * @return True if a layout algorithm is currenly running, false otherwise >- */ >- public boolean isRunning(); >- >- /** >- * Determines the order in which the objects should be displayed. >- * Note: Some algorithms force a specific order, in which case >- * this comparator will be ignored. >- */ >- public void setComparator(Comparator comparator); >- >- /** >- * Filters the entities and relationships to apply the layout on >- */ >- public void setFilter(Filter filter); >- >- /** >- * Set the width to height ratio you want the entities to use >- * Note: Each layout is responsible for ensuring this ratio is used. >- * Note: By default the layout will use a ratio of 1.0 for each entity. >- */ >- public void setEntityAspectRatio(double ratio); >- >- /** >- * Returns the width to height ratio this layout will use to set the size of the entities. >- * Note: By default the layout will use a ratio of 1.0 for each entity. >- */ >- public double getEntityAspectRatio(); >- >- /** >- * A layout algorithm could take an uncomfortable amout of time to complete. To relieve some of >- * the mystery, the layout algorithm will notify each ProgressListener of its progress. >- */ >- public void addProgressListener(ProgressListener listener); >- >- /** >- * Removes the given progress listener, preventing it from receiving any more updates. >- */ >- public void removeProgressListener(ProgressListener listener); >- >- /** >- * Makes a request to this layout algorithm to stop running. >- */ >- public void stop(); >- >- /** >- * Sets the style for this layout algorithm. This will overwrite any other style set. >- * @param style >- */ >- public void setStyle(int style); >- >- /** >- * >- * @return >- */ >- public int getStyle(); >- >- public void addEntity(LayoutEntity entity); >- >- public void addRelationship(LayoutRelationship relationship); >- >- public void removeEntity(LayoutEntity entity); >- >- public void removeRelationship(LayoutRelationship relationship); >- >- public void removeRelationships(List relationships); >- >-} >+/******************************************************************************* >+ * Copyright (c) 2005-2010 The Chisel Group and others. 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 - initial API and implementation >+ * Mateusz Matela >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.layouts; >+ >+import org.eclipse.zest.layouts.interfaces.LayoutContext; >+ >+/** >+ * An interface for all layout algorithms. >+ * >+ * >+ */ >+public interface LayoutAlgorithm { >+ >+ /** >+ * Sets the layout context for this algorithm. The receiver will unregister >+ * from its previous layout context and register to the new one >+ * (registration means for example adding listeners). After a call to this >+ * method, the receiving algorithm can compute and cache internal data >+ * related to given context and perform an initial layout. >+ * >+ * @param context >+ * a new layout context or null if this algorithm should not >+ * perform any layout >+ */ >+ public void setLayoutContext(LayoutContext context); >+ >+ /** >+ * Makes this algorithm perform layout computation and apply it to its >+ * context. >+ * >+ * @param clean >+ * if true the receiver should assume that the layout context has >+ * changed significantly and recompute the whole layout even if >+ * it keeps track of changes with listeners. False can be used >+ * after dynamic layout in a context is turned back on so that >+ * layout algorithm working in background can apply accumulated >+ * changes. Static layout algorithm can ignore this call entirely >+ * if clean is false. >+ */ >+ public void applyLayout(boolean clean); >+ >+} >Index: src/org/eclipse/zest/layouts/LayoutBendPoint.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/LayoutBendPoint.java >diff -N src/org/eclipse/zest/layouts/LayoutBendPoint.java >--- src/org/eclipse/zest/layouts/LayoutBendPoint.java 12 Sep 2007 20:44:37 -0000 1.4 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,22 +0,0 @@ >-/******************************************************************************* >- * Copyright 2006, 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.layouts; >- >-/** >- * Specifies a single bend point in a graph relationship. >- * @author Ian Bull >- * @author Chris Bennett >- */ >-public interface LayoutBendPoint { >- public double getX(); >- public double getY(); >- public boolean getIsControlPoint(); >-} >Index: src/org/eclipse/zest/layouts/LayoutEntity.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/LayoutEntity.java >diff -N src/org/eclipse/zest/layouts/LayoutEntity.java >--- src/org/eclipse/zest/layouts/LayoutEntity.java 12 Jan 2008 01:42:53 -0000 1.6 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,44 +0,0 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts; >- >-import org.eclipse.zest.layouts.constraints.LayoutConstraint; >- >-/** >- * This represents a single entity, providing the layout algorithms with >- * a common interface to run on. >- * >- * @author Casey Best >- * @author Ian Bull >- * @author Chris Bennett >- */ >-public interface LayoutEntity extends Comparable, LayoutItem { >- >- public final static String ATTR_PREFERRED_WIDTH = "tree-preferred-width"; >- public final static String ATTR_PREFERRED_HEIGHT = "tree-preferred-height"; >- >- public void setLocationInLayout (double x, double y); >- public void setSizeInLayout (double width, double height); >- >- public double getXInLayout(); >- public double getYInLayout(); >- public double getWidthInLayout(); >- public double getHeightInLayout(); >- >- public Object getLayoutInformation(); >- public void setLayoutInformation(Object internalEntity); >- >- /** >- * Classes should update the specified layout constraint if recognized >- * @return >- */ >- public void populateLayoutConstraint(LayoutConstraint constraint); >-} >Index: src/org/eclipse/zest/layouts/LayoutGraph.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/LayoutGraph.java >diff -N src/org/eclipse/zest/layouts/LayoutGraph.java >--- src/org/eclipse/zest/layouts/LayoutGraph.java 12 Sep 2007 20:44:37 -0000 1.3 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,52 +0,0 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts; >- >-import java.util.List; >- >-/** >- * The LayoutGraph interface defines the methods used to add nodes and edges (relationships). >- * @author Chris >- */ >-public interface LayoutGraph { >- >- /** >- * Adds a node to this graph. >- * @param node The new node. >- * @return LayoutEntity The created node >- */ >- public void addEntity(LayoutEntity node); >- >- /** >- * Adds the given relationship. >- * @param relationship >- */ >- public void addRelationship(LayoutRelationship relationship); >- >- /** >- * Returns a list of LayoutEntity objects that represent the objects added to this graph using addNode. >- * @return List A List of LayoutEntity objects. >- */ >- public List getEntities(); >- >- /** >- * Returns a list of LayoutRelationship objects that represent the objects added to this graph using addRelationship. >- * @return List A List of LayoutRelationship objects. >- */ >- public List getRelationships(); >- >- /** >- * Determines if the graph is bidirectional. >- * @return boolean If the graph is bidirectional. >- */ >- public boolean isBidirectional(); >- >-} >Index: src/org/eclipse/zest/layouts/LayoutItem.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/LayoutItem.java >diff -N src/org/eclipse/zest/layouts/LayoutItem.java >--- src/org/eclipse/zest/layouts/LayoutItem.java 12 Jan 2008 01:42:28 -0000 1.1 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,24 +0,0 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts; >- >-/** >- * Super interface for both Layout Entities and Layout Relationships >- * >- * @author Ian Bull >- * >- */ >-public interface LayoutItem { >- >- public void setGraphData(Object o); >- public Object getGraphData(); >- >-} >Index: src/org/eclipse/zest/layouts/LayoutIterationEvent.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/LayoutIterationEvent.java >diff -N src/org/eclipse/zest/layouts/LayoutIterationEvent.java >--- src/org/eclipse/zest/layouts/LayoutIterationEvent.java 12 Sep 2007 20:44:37 -0000 1.3 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,49 +0,0 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts; >- >-import java.util.List; >- >-/** >- * When a layout completes an iteration, it throws this event >- * to allow the application to update. For example, at the >- * end of an iteration is can be assumed the layout has placed >- * each entity into a new location. This event allows the application >- * to update the GUI to represent the new locations >- * >- * @author Casey Best and Rob Lintern >- */ >-public class LayoutIterationEvent { >- private List relationshipsToLayout, entitiesToLayout; >- private int iterationCompleted; >- >- /** >- * Return the relationships used in this layout. >- */ >- public List getRelationshipsToLayout() { >- return relationshipsToLayout; >- } >- >- /** >- * Return the entities used in this layout. >- */ >- public List getEntitiesToLayout() { >- return entitiesToLayout; >- } >- >- /** >- * Return the iteration of the layout algorithm that was >- * just completed. >- */ >- public int getIterationCompleted() { >- return iterationCompleted; >- } >-} >Index: src/org/eclipse/zest/layouts/LayoutRelationship.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/LayoutRelationship.java >diff -N src/org/eclipse/zest/layouts/LayoutRelationship.java >--- src/org/eclipse/zest/layouts/LayoutRelationship.java 12 Jan 2008 01:42:53 -0000 1.9 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,82 +0,0 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts; >- >-import org.eclipse.zest.layouts.constraints.LayoutConstraint; >- >- >- >-/** >- * This represents a single relationship, providing the layout algorithms with >- * a common interface to run on. >- * >- * @author Casey Best >- * @author Chris Callendar >- */ >-public interface LayoutRelationship extends LayoutItem { >- >- >- /** >- * Gets the sourceEntity of this SimpleRelation whether the relation is >- * exchangeable or not. >- * @return The sourceEntity. >- */ >- public LayoutEntity getSourceInLayout(); >- >- /** >- * Gets the destinationEntity of this SimpleRelation whether the relation is >- * exchangeable or not. >- * @return The destinationEntity of this SimpleRelation. >- */ >- public LayoutEntity getDestinationInLayout(); >- >- >- /** >- * Sets the internal relationship object. >- * @param layoutInformation >- */ >- public void setLayoutInformation(Object layoutInformation); >- >- /** >- * Returns the internal relationship object. >- * @return Object >- */ >- public Object getLayoutInformation(); >- >- /** >- * Specify a set of bend points. The layout algorithm using this will pass >- * in an empty array of bendPoints, or not even call this method, >- * if there are no bend points associated with this edge. >- * >- * If you are updating an existing application you can just implement this >- * method to do nothing. >- * >- * @param bendPoints A list of bend points. All bendpoint locations are expressed >- * as percentages of the bounds (0,0 to 1,1).The first bendpoint in the list must be the >- * source point of this relationship and the last bendpoint the destination point >- * for this relationship. This allows the correct positioning of bendpoints >- * relative to the source and destination points when drawing the graph. >- */ >- public void setBendPoints(LayoutBendPoint[] bendPoints); >- >- /** >- * Clear bend points and related bounds >- * If you are updating an existing application you can just implement this >- * method to do nothing. >- */ >- public void clearBendPoints(); >- >- /** >- * Classes should update the specirfied layout constraint if recognized >- * @return >- */ >- public void populateLayoutConstraint(LayoutConstraint constraint); >-} >Index: src/org/eclipse/zest/layouts/LayoutStyles.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/LayoutStyles.java >diff -N src/org/eclipse/zest/layouts/LayoutStyles.java >--- src/org/eclipse/zest/layouts/LayoutStyles.java 12 Sep 2007 20:44:38 -0000 1.5 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,33 +0,0 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts; >- >-/** >- * @author Ian Bull >- */ >-public interface LayoutStyles { >- >- /** Default layout style constant. */ >- public final static int NONE = 0x00; >- >- /** >- * Layout constant indicating that the layout algorithm >- * should NOT resize any of the nodes. >- */ >- public final static int NO_LAYOUT_NODE_RESIZING = 0x01; >- >- /** >- * Some layouts may prefer to expand their bounds beyond those of the requested bounds. This >- * flag asks the layout not to do so. >- */ >- public static final int ENFORCE_BOUNDS = 0X02; >- >-} >Index: src/org/eclipse/zest/layouts/NestedLayoutEntity.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/NestedLayoutEntity.java >diff -N src/org/eclipse/zest/layouts/NestedLayoutEntity.java >--- src/org/eclipse/zest/layouts/NestedLayoutEntity.java 12 Sep 2007 20:44:37 -0000 1.4 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,32 +0,0 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts; >- >-import java.util.List; >- >- >-/** >- * Extends LayoutEntity to provide methods for dealing with nested entities. >- * >- * @author Chris Callendar >- */ >-public interface NestedLayoutEntity extends LayoutEntity { >- >- /** Returns the parent entity. */ >- NestedLayoutEntity getParent(); >- >- /** Returns the list of children. */ >- List getChildren(); >- >- /** Returns true if this entity has children. */ >- boolean hasChildren(); >- >-} >Index: src/org/eclipse/zest/layouts/Stoppable.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/Stoppable.java >diff -N src/org/eclipse/zest/layouts/Stoppable.java >--- src/org/eclipse/zest/layouts/Stoppable.java 12 Sep 2007 20:44:37 -0000 1.5 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,27 +0,0 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts; >- >-import org.eclipse.zest.layouts.progress.ProgressListener; >- >-/** >- * @author Ian Bull >- */ >-public interface Stoppable { >- >- /** >- * This ends the runnable >- */ >- public void stop(); >- >- public void addProgressListener(ProgressListener listener); >- >-} >Index: src/org/eclipse/zest/layouts/algorithms/AbstractLayoutAlgorithm.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/algorithms/AbstractLayoutAlgorithm.java >diff -N src/org/eclipse/zest/layouts/algorithms/AbstractLayoutAlgorithm.java >--- src/org/eclipse/zest/layouts/algorithms/AbstractLayoutAlgorithm.java 12 Jan 2008 01:34:29 -0000 1.24 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,1035 +0,0 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts.algorithms; >- >-import java.util.ArrayList; >-import java.util.Arrays; >-import java.util.Calendar; >-import java.util.Collection; >-import java.util.Comparator; >-import java.util.Iterator; >-import java.util.List; >- >-import org.eclipse.zest.layouts.Filter; >-import org.eclipse.zest.layouts.InvalidLayoutConfiguration; >-import org.eclipse.zest.layouts.LayoutAlgorithm; >-import org.eclipse.zest.layouts.LayoutEntity; >-import org.eclipse.zest.layouts.LayoutItem; >-import org.eclipse.zest.layouts.LayoutRelationship; >-import org.eclipse.zest.layouts.LayoutStyles; >-import org.eclipse.zest.layouts.Stoppable; >-import org.eclipse.zest.layouts.constraints.BasicEntityConstraint; >-import org.eclipse.zest.layouts.dataStructures.BendPoint; >-import org.eclipse.zest.layouts.dataStructures.DisplayIndependentDimension; >-import org.eclipse.zest.layouts.dataStructures.DisplayIndependentPoint; >-import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle; >-import org.eclipse.zest.layouts.dataStructures.InternalNode; >-import org.eclipse.zest.layouts.dataStructures.InternalRelationship; >-import org.eclipse.zest.layouts.progress.ProgressEvent; >-import org.eclipse.zest.layouts.progress.ProgressListener; >- >-/** >- * Handles common elements in all layout algorithms >- * [irbull] Refactored into a template pattern. ApplyLayout now delegates the >- * task to ApplyLayoutInternal >- * >- * [irbull] Included asynchronous layouts >- * >- * @version 1.0 >- * @author Casey Best >- * @author Ian Bull >- * @author Chris Callendar >- * @author Rob Lintern >- * @author Chris Bennett >- */ >-public abstract class AbstractLayoutAlgorithm implements LayoutAlgorithm, Stoppable { >- >- public void removeRelationships(Collection collection) { >- >- } >- >- public final static int MIN_ENTITY_SIZE = 5; >- private final static int MIN_TIME_DELAY_BETWEEN_PROGRESS_EVENTS = 1; >- >- private Thread creationThread = null; >- protected Comparator comparator; >- protected Filter filter; >- private List progressListeners; >- private Calendar lastProgressEventFired; >- private double widthToHeightRatio; >- >- class InternalComparator implements Comparator { >- Comparator externalComparator = null; >- >- public InternalComparator(Comparator externalComparator) { >- this.externalComparator = externalComparator; >- } >- >- public int compare(Object o1, Object o2) { >- InternalNode internalNode1 = (InternalNode) o1; >- InternalNode internalNode2 = (InternalNode) o2; >- >- return this.externalComparator.compare(internalNode1.getLayoutEntity(), internalNode2.getLayoutEntity()); >- } >- >- } >- >- /* >- * Internal Nodes. >- */ >- private InternalNode[] internalNodes; >- private InternalRelationship[] internalRelationships; >- private double internalX; >- private double internalY; >- private double internalWidth; >- private double internalHeight; >- protected boolean internalContinuous; >- protected boolean internalAsynchronous; >- >- /* >- * A queue of entities and relationships to add or remove. Each layout >- * algorithm should check these and update their internal lists. >- */ >- >- /** A list of LayoutEntity objects to be removed from the layout. */ >- private List entitiesToRemove; >- /** A list of LayoutRelationship objects to be removed. */ >- private List relationshipsToRemove; >- /** A list of LayoutEntity objects to be added to the layout. */ >- private List entitiesToAdd; >- /** A list of LayoutRelationship objects to be added. */ >- private List relationshipsToAdd; >- >- //protected boolean cancelled = false; >- >- protected boolean layoutStopped = true; >- >- protected int layout_styles = 0; >- >- // Child classes can set to false to retain node shapes and sizes >- protected boolean resizeEntitiesAfterLayout = true; >- >- /** >- * Initializes the abstract layout algorithm. >- * @see LayoutStyles >- */ >- public AbstractLayoutAlgorithm(int styles) { >- this.creationThread = Thread.currentThread(); >- this.progressListeners = new ArrayList(); >- this.lastProgressEventFired = Calendar.getInstance(); >- this.widthToHeightRatio = 1.0; >- >- this.entitiesToRemove = new ArrayList(); >- this.relationshipsToRemove = new ArrayList(); >- this.entitiesToAdd = new ArrayList(); >- this.relationshipsToAdd = new ArrayList(); >- this.layout_styles = styles; >- } >- >- /** >- * Queues up the given entity (if it isn't in the list) to be added to the algorithm. >- * @param entity >- */ >- public void addEntity(LayoutEntity entity) { >- if ((entity != null) && !entitiesToAdd.contains(entity)) { >- entitiesToAdd.add(entity); >- } >- } >- >- /** >- * Queues up the given relationshp (if it isn't in the list) to be added to the algorithm. >- * @param relationship >- */ >- public void addRelationship(LayoutRelationship relationship) { >- if ((relationship != null) && !relationshipsToAdd.contains(relationship)) { >- relationshipsToAdd.add(relationship); >- } >- } >- >- /** >- * Queues up the given entity to be removed from the algorithm next time it runs. >- * @param entity The entity to remove >- */ >- public void removeEntity(LayoutEntity entity) { >- if ((entity != null) && !entitiesToRemove.contains(entity)) { >- entitiesToRemove.add(entity); >- } >- } >- >- /** >- * Queues up the given relationship to be removed from the algorithm next time it runs. >- * @param relationship The relationship to remove. >- */ >- public void removeRelationship(LayoutRelationship relationship) { >- if ((relationship != null) && !relationshipsToRemove.contains(relationship)) { >- relationshipsToRemove.add(relationship); >- } >- } >- >- /** >- * Queues up all the relationships in the list to be removed. >- * @param relationships >- */ >- public void removeRelationships(List relationships) { >- // note we don't check if the relationshipsToRemove contains >- // any of the objects in relationships. >- relationshipsToRemove.addAll(relationships); >- } >- >- /** >- * Sets the current layout style. This overwrites all other layout styles. >- * Use getStyle to get the current style. >- * @param style >- */ >- public void setStyle(int style) { >- this.layout_styles = style; >- } >- >- /** >- * Gets the current layout style >- * @return >- */ >- public int getStyle() { >- return this.layout_styles; >- } >- >- public abstract void setLayoutArea(double x, double y, double width, double height); >- >- /** >- * Determines if the configuration is valid for this layout >- * @param asynchronous >- * @param continuous >- */ >- protected abstract boolean isValidConfiguration(boolean asynchronous, boolean continuous); >- >- /** >- * Apply the layout to the given entities. The entities will be moved and resized based >- * on the algorithm. >- * >- * @param entitiesToLayout Apply the algorithm to these entities >- * @param relationshipsToConsider Only consider these relationships when applying the algorithm. >- * @param x The left side of the bounds in which the layout can place the entities. >- * @param y The top side of the bounds in which the layout can place the entities. >- * @param width The width of the bounds in which the layout can place the entities. >- * @param height The height of the bounds in which the layout can place the entities. >- */ >- abstract protected void applyLayoutInternal(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double boundsX, double boundsY, double boundsWidth, double boundsHeight); >- >- /** >- * Updates the given array of entities checking if any need to be removed or added. >- * @param entities the current entities >- * @return the updated entities array >- */ >- protected InternalNode[] updateEntities(InternalNode[] entities) { >- if ((entitiesToRemove.size() > 0) || (entitiesToAdd.size() > 0)) { >- List internalNodesList = new ArrayList(Arrays.asList(entities)); >- >- // remove nodes >- for (Iterator iter = entitiesToRemove.iterator(); iter.hasNext();) { >- LayoutEntity entity = (LayoutEntity) iter.next(); >- if (entity.getLayoutInformation() != null) { >- internalNodesList.remove(entity.getLayoutInformation()); >- } >- } >- >- // Also remove from _internalNodes >- ArrayList updatedEntities = new ArrayList(internalNodes.length - entitiesToRemove.size() + entitiesToAdd.size()); >- for (int i = 0; i < internalNodes.length; i++) { >- InternalNode node = internalNodes[i]; >- if (entitiesToRemove.contains(node.getLayoutEntity())) { >- entitiesToRemove.remove(node.getLayoutEntity()); >- } else { >- updatedEntities.add(node); >- } >- } >- entitiesToRemove.clear(); >- >- // Add any new nodes >- LayoutEntity[] entitiesArray = new LayoutEntity[entitiesToAdd.size()]; >- entitiesArray = (LayoutEntity[]) entitiesToAdd.toArray(entitiesArray); >- InternalNode[] newNodes = createInternalNodes(entitiesArray); >- for (int i = 0; i < newNodes.length; i++) { >- internalNodesList.add(newNodes[i]); >- updatedEntities.add(newNodes[i]); >- } >- entitiesToAdd.clear(); >- >- entities = new InternalNode[internalNodesList.size()]; >- entities = (InternalNode[]) internalNodesList.toArray(entities); >- >- internalNodes = new InternalNode[updatedEntities.size()]; >- internalNodes = (InternalNode[]) updatedEntities.toArray(internalNodes); >- } >- >- return entities; >- } >- >- /** >- * Updates the given array of relationships checking if any need to be removed or added. >- * Also updates the original array of relationships. >- * @param relationships the current relationships >- * @return the update relationships array >- */ >- protected InternalRelationship[] updateRelationships(InternalRelationship[] relationships) { >- if ((relationshipsToRemove.size() > 0) || (relationshipsToAdd.size() > 0)) { >- List internalRelsList = new ArrayList(Arrays.asList(relationships)); >- >- // remove relationships >- if (relationshipsToRemove.size() > 0) { >- for (Iterator iter = relationshipsToRemove.iterator(); iter.hasNext();) { >- LayoutRelationship relation = (LayoutRelationship) iter.next(); >- if (relation.getLayoutInformation() != null) { >- internalRelsList.remove(relation.getLayoutInformation()); >- } >- } >- } >- >- // Also remove from _internalRelationships >- ArrayList updatedRelationships = new ArrayList(internalRelationships.length - relationshipsToRemove.size() + relationshipsToAdd.size()); >- for (int i = 0; i < internalRelationships.length; i++) { >- InternalRelationship relation = internalRelationships[i]; >- if (relationshipsToRemove.contains(relation.getLayoutRelationship())) { >- relationshipsToRemove.remove(relation.getLayoutRelationship()); >- } else { >- updatedRelationships.add(relation); >- } >- } >- relationshipsToRemove.clear(); >- >- // add relationships >- if (relationshipsToAdd.size() > 0) { >- LayoutRelationship[] relsArray = new LayoutRelationship[relationshipsToAdd.size()]; >- relsArray = (LayoutRelationship[]) relationshipsToAdd.toArray(relsArray); >- InternalRelationship[] newRelationships = createInternalRelationships(relsArray); >- for (int i = 0; i < newRelationships.length; i++) { >- internalRelsList.add(newRelationships[i]); >- updatedRelationships.add(newRelationships[i]); >- } >- } >- relationshipsToAdd.clear(); >- >- relationships = new InternalRelationship[internalRelsList.size()]; >- relationships = (InternalRelationship[]) internalRelsList.toArray(relationships); >- >- internalRelationships = new InternalRelationship[updatedRelationships.size()]; >- internalRelationships = (InternalRelationship[]) updatedRelationships.toArray(internalRelationships); >- } >- >- return relationships; >- } >- >- /** >- * Moves all the entities by the given amount. >- * @param dx the amount to shift the entities in the x-direction >- * @param dy the amount to shift the entities in the y-direction >- */ >- /* >- public void moveAllEntities(double dx, double dy) { >- if ((dx != 0) || (dy != 0)) { >- synchronized (_internalNodes) { >- for (int i = 0; i < _internalNodes.length; i++) { >- InternalNode node = _internalNodes[i]; >- node.setInternalLocation(node.getInternalX()+dx, node.getInternalY()+dy); >- node.setLocation(node.getX()+dx, node.getY()+dy); >- } >- } >- } >- } >- */ >- >- /** >- * Returns true if the layout algorithm is running >- * @return boolean if the layout algorithm is running >- */ >- public synchronized boolean isRunning() { >- return !layoutStopped; >- } >- >- /** >- * Stops the current layout from running. >- * All layout algorithms should constantly check isLayoutRunning >- */ >- public synchronized void stop() { >- layoutStopped = true; >- postLayoutAlgorithm(internalNodes, internalRelationships); >- fireProgressEnded(getTotalNumberOfLayoutSteps()); >- } >- >- // /** >- // * Sleeps while the algorithm is paused. >- // */ >- // protected void sleepWhilePaused() { >- // // do nothing while the algorithm is paused >- // boolean wasPaused = false; >- // while (isPaused()) { >- // try { >- // Thread.sleep(200); >- // } catch (InterruptedException e) { >- // } >- // wasPaused = true; >- // } >- // // update the node positions (they might have been moved while paused) >- // if (wasPaused) { >- // for (int i = 0; i < internalNodes.length; i++) { >- // InternalNode node = internalNodes[i]; >- // node.setInternalLocation(node.getPreferredX(), node.getPreferredY()); >- // } >- // } >- // } >- >- private void setupLayout(LayoutEntity[] entitiesToLayout, LayoutRelationship[] relationshipsToConsider, double x, double y, double width, double height) { >- internalX = x; >- internalY = y; >- internalHeight = height; >- internalWidth = width; >- // Filter all the unwanted entities and relationships >- entitiesToLayout = (LayoutEntity[]) filterUnwantedObjects(entitiesToLayout); >- relationshipsToConsider = (LayoutRelationship[]) filterUnwantedObjects(relationshipsToConsider); >- >- // Check that the input is valid >- if (!verifyInput(entitiesToLayout, relationshipsToConsider)) { >- layoutStopped = true; >- throw new RuntimeException("The relationships in relationshipsToConsider don't contain the entities in entitiesToLayout"); >- } >- >- // Create the internal nodes and relationship >- internalNodes = createInternalNodes(entitiesToLayout); >- internalRelationships = createInternalRelationships(relationshipsToConsider); >- } >- >- // public synchronized Stoppable getLayoutThread(LayoutEntity[] entitiesToLayout, LayoutRelationship[] relationshipsToConsider, double x, double y, double width, double height, boolean continuous) { >- // //setupLayout( entitiesToLayout, relationshipsToConsider, x, y, width, height ); >- // this.layoutStopped = false; >- // this.runContinuously = continuous; >- // setupLayout(entitiesToLayout, relationshipsToConsider, x, y, width, height); >- // preLayoutAlgorithm(internalNodes, internalRelationships, internalX, internalY, internalWidth, internalHeight); >- // fireProgressStarted(getTotalNumberOfLayoutSteps()); >- // return this; >- // } >- >- /** >- * Code called before the layout algorithm starts >- */ >- protected abstract void preLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height); >- >- /** >- * Code called after the layout algorithm ends >- */ >- protected abstract void postLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider); >- >- /** >- * Gets the total number of steps in this layout >- */ >- protected abstract int getTotalNumberOfLayoutSteps(); >- >- /** >- * Gets the current layout step >- * @return >- */ >- protected abstract int getCurrentLayoutStep(); >- >- /** >- * This actually applies the layout >- */ >- public synchronized void applyLayout(final LayoutEntity[] entitiesToLayout, final LayoutRelationship[] relationshipsToConsider, final double x, final double y, final double width, final double height, boolean asynchronous, boolean continuous) throws InvalidLayoutConfiguration { >- checkThread(); >- this.internalAsynchronous = asynchronous; >- this.internalContinuous = continuous; >- >- if (!isValidConfiguration(asynchronous, continuous)) { >- throw new InvalidLayoutConfiguration(); >- } >- >- clearBendPoints(relationshipsToConsider); >- >- this.layoutStopped = false; >- >- // when an algorithm starts, reset the progress event >- lastProgressEventFired = Calendar.getInstance(); >- if (asynchronous) { >- >- Thread thread = new Thread(new Runnable() { >- >- public void run() { >- setupLayout(entitiesToLayout, relationshipsToConsider, x, y, width, height); >- preLayoutAlgorithm(internalNodes, internalRelationships, internalX, internalY, internalWidth, internalHeight); >- fireProgressStarted(getTotalNumberOfLayoutSteps()); >- >- applyLayoutInternal(internalNodes, internalRelationships, internalX, internalY, internalWidth, internalHeight); >- stop(); >- } >- >- }); >- thread.setPriority(Thread.MIN_PRIORITY); >- thread.start(); >- } else { >- >- // If we are running synchronously then we have to stop this at some >- // point? right? >- setupLayout(entitiesToLayout, relationshipsToConsider, x, y, width, height); >- preLayoutAlgorithm(internalNodes, internalRelationships, internalX, internalY, internalWidth, internalHeight); >- fireProgressStarted(getTotalNumberOfLayoutSteps()); >- >- applyLayoutInternal(internalNodes, internalRelationships, internalX, internalY, internalWidth, internalHeight); >- stop(); >- } >- >- } >- >- /** >- * Clear out all old bend points before doing a layout >- */ >- private void clearBendPoints(LayoutRelationship[] relationships) { >- for (int i = 0; i < relationships.length; i++) { >- LayoutRelationship rel = relationships[i]; >- rel.clearBendPoints(); >- } >- } >- >- /** >- * Update external bend points from the internal bendpoints list. Save the >- * source and destination points for later use in scaling and translating >- * @param relationshipsToConsider >- */ >- protected void updateBendPoints(InternalRelationship[] relationshipsToConsider) { >- for (int i = 0; i < relationshipsToConsider.length; i++) { >- InternalRelationship relationship = relationshipsToConsider[i]; >- List bendPoints = relationship.getBendPoints(); >- if (bendPoints.size() > 0) { >- // We will assume that source/dest coordinates are for center of node >- BendPoint[] externalBendPoints = new BendPoint[bendPoints.size() + 2]; >- InternalNode sourceNode = relationship.getSource(); >- externalBendPoints[0] = new BendPoint(sourceNode.getInternalX(), sourceNode.getInternalY()); >- InternalNode destNode = relationship.getDestination(); >- externalBendPoints[externalBendPoints.length - 1] = new BendPoint(destNode.getInternalX(), destNode.getInternalY()); >- >- for (int j = 0; j < bendPoints.size(); j++) { >- BendPoint bp = (BendPoint) bendPoints.get(j); >- externalBendPoints[j + 1] = new BendPoint(bp.x, bp.y, bp.getIsControlPoint()); >- } >- relationship.getLayoutRelationship().setBendPoints(externalBendPoints); >- } >- } >- } >- >- // public void run() { >- // >- // if (started == true) { >- // throw new RuntimeException("Layout has already run!"); >- // } >- // started = true; >- // //layoutStopped = false; >- // isLayoutPaused = false; >- // applyLayoutInternal(internalNodes, internalRelationships, internalX, internalY, internalWidth, internalHeight); >- // stop(); >- // layoutStopped = true; >- // isLayoutPaused = false; >- // } >- >- /** >- * Creates a list of InternalNode objects from the list of LayoutEntity objects the user >- * wants layed out. Sets the internal nodes' positions and sizes from the >- * external entities. >- */ >- private InternalNode[] createInternalNodes(LayoutEntity[] nodes) { >- InternalNode[] internalNodes = new InternalNode[nodes.length]; >- BasicEntityConstraint basicEntityConstraint = new BasicEntityConstraint(); >- for (int i = 0; i < nodes.length; i++) { >- basicEntityConstraint.clear(); >- LayoutEntity externalNode = nodes[i]; >- InternalNode internalNode = new InternalNode(externalNode); >- externalNode.populateLayoutConstraint(basicEntityConstraint); >- internalNode.setInternalLocation(externalNode.getXInLayout(), externalNode.getYInLayout()); >- internalNodes[i] = internalNode; >- } // end of for >- return internalNodes; >- } >- >- /** >- * Creates a list of InternalRelationship objects from the given list of LayoutRelationship objects. >- * @param rels >- * @return List of internal relationships >- */ >- private InternalRelationship[] createInternalRelationships(LayoutRelationship[] rels) { >- ArrayList listOfInternalRelationships = new ArrayList(rels.length); >- for (int i = 0; i < rels.length; i++) { >- LayoutRelationship relation = rels[i]; >- InternalNode src = (InternalNode) relation.getSourceInLayout().getLayoutInformation(); >- InternalNode dest = (InternalNode) relation.getDestinationInLayout().getLayoutInformation(); >- if ((src != null) && (dest != null)) { >- InternalRelationship internalRelationship = new InternalRelationship(relation, src, dest); >- listOfInternalRelationships.add(internalRelationship); >- } else { >- throw new RuntimeException("Error creating internal relationship, one of the nodes is null: src=" + src + ", dest=" + dest); >- } >- } >- InternalRelationship[] internalRelationships = new InternalRelationship[listOfInternalRelationships.size()]; >- listOfInternalRelationships.toArray(internalRelationships); >- return internalRelationships; >- } >- >- /** >- * Removes any objects that are currently filtered >- */ >- private Object[] filterUnwantedObjects(LayoutItem[] objects) { >- // first remove any entities or relationships that are filtered. >- List unfilteredObjsList = new ArrayList(); >- if (filter != null) { >- for (int i = 0; i < objects.length; i++) { >- LayoutItem object = objects[i]; >- if (!filter.isObjectFiltered(object)) { >- unfilteredObjsList.add(object); >- } >- } >- //@tag bug.156266-ClassCast.fix : use reflection to create the array. >- Object[] unfilteredObjs = (Object[]) java.lang.reflect.Array.newInstance(objects.getClass().getComponentType(), unfilteredObjsList.size()); >- unfilteredObjsList.toArray(unfilteredObjs); >- return unfilteredObjs; >- } >- return objects; >- } >- >- /** >- * Filters the entities and relationships to apply the layout on >- */ >- public void setFilter(Filter filter) { >- this.filter = filter; >- } >- >- /** >- * Determines the order in which the objects should be displayed. >- * Note: Some algorithms force a specific order. >- */ >- public void setComparator(Comparator comparator) { >- this.comparator = new InternalComparator(comparator); >- } >- >- /** >- * Verifies the endpoints of the relationships are entities in the entitiesToLayout list. >- * Allows other classes in this package to use this method to verify the input >- */ >- public static boolean verifyInput(LayoutEntity[] entitiesToLayout, LayoutRelationship[] relationshipsToConsider) { >- boolean stillValid = true; >- for (int i = 0; i < relationshipsToConsider.length; i++) { >- LayoutRelationship relationship = relationshipsToConsider[i]; >- LayoutEntity source = relationship.getSourceInLayout(); >- LayoutEntity destination = relationship.getDestinationInLayout(); >- boolean containsSrc = false; >- boolean containsDest = false; >- int j = 0; >- while (j < entitiesToLayout.length && !(containsSrc && containsDest)) { >- if (entitiesToLayout[j].equals(source)) { >- containsSrc = true; >- } >- if (entitiesToLayout[j].equals(destination)) { >- containsDest = true; >- } >- j++; >- } >- stillValid = containsSrc && containsDest; >- } >- return stillValid; >- } >- >- /** >- * Gets the location in the layout bounds for this node >- * @param x >- * @param y >- * @return >- */ >- protected DisplayIndependentPoint getLocalLocation(InternalNode[] entitiesToLayout, double x, double y, DisplayIndependentRectangle realBounds) { >- >- double screenWidth = realBounds.width; >- double screenHeight = realBounds.height; >- DisplayIndependentRectangle layoutBounds = getLayoutBounds(entitiesToLayout, false); >- double localX = (x / screenWidth) * layoutBounds.width + layoutBounds.x; >- double localY = (y / screenHeight) * layoutBounds.height + layoutBounds.y; >- return new DisplayIndependentPoint(localX, localY); >- } >- >- /** >- * Find an appropriate size for the given nodes, then fit them into the given bounds. >- * The relative locations of the nodes to each other must be preserved. >- * Child classes should set flag reresizeEntitiesAfterLayout to false if they >- * want to preserve node sizes. >- */ >- protected void defaultFitWithinBounds(InternalNode[] entitiesToLayout, DisplayIndependentRectangle realBounds) { >- defaultFitWithinBounds(entitiesToLayout, new InternalRelationship[0], realBounds); >- } >- >- /** >- * Find an appropriate size for the given nodes, then fit them into the given bounds. >- * The relative locations of the nodes to each other must be preserved. >- * Child classes should set flag reresizeEntitiesAfterLayout to false if they >- * want to preserve node sizes. >- */ >- protected void defaultFitWithinBounds(InternalNode[] entitiesToLayout, InternalRelationship[] relationships, DisplayIndependentRectangle realBounds) { >- >- DisplayIndependentRectangle layoutBounds; >- >- if (resizeEntitiesAfterLayout) { >- layoutBounds = getLayoutBounds(entitiesToLayout, false); >- >- // Convert node x,y to be in percent rather than absolute coords >- convertPositionsToPercentage(entitiesToLayout, relationships, layoutBounds, false /*do not update size*/); >- >- // Resize and shift nodes >- resizeAndShiftNodes(entitiesToLayout); >- } >- >- // Recalculate layout, allowing for the node width, which we now know >- layoutBounds = getLayoutBounds(entitiesToLayout, true); >- >- // adjust node positions again, to the new coordinate system (still as a percentage) >- convertPositionsToPercentage(entitiesToLayout, relationships, layoutBounds, true /*update node size*/); >- >- DisplayIndependentRectangle screenBounds = calcScreenBounds(realBounds, layoutBounds); >- >- // Now convert to real screen coordinates >- convertPositionsToCoords(entitiesToLayout, relationships, screenBounds); >- } >- >- /** >- * Calculate the screen bounds, maintaining the >- * @param realBounds >- * @return >- */ >- private DisplayIndependentRectangle calcScreenBounds(DisplayIndependentRectangle realBounds, DisplayIndependentRectangle layoutBounds) { >- if (resizeEntitiesAfterLayout) { // OK to alter aspect ratio >- double borderWidth = Math.min(realBounds.width, realBounds.height) / 10.0; // use 10% for the border - 5% on each side >- return new DisplayIndependentRectangle(realBounds.x + borderWidth / 2.0, realBounds.y + borderWidth / 2.0, realBounds.width - borderWidth, realBounds.height - borderWidth); >- } else { // retain layout aspect ratio >- double heightAdjustment = realBounds.height / layoutBounds.height; >- double widthAdjustment = realBounds.width / layoutBounds.width; >- double ratio = Math.min(heightAdjustment, widthAdjustment); >- double adjustedHeight = layoutBounds.height * ratio; >- double adjustedWidth = layoutBounds.width * ratio; >- double adjustedX = realBounds.x + (realBounds.width - adjustedWidth) / 2.0; >- double adjustedY = realBounds.y + (realBounds.height - adjustedHeight) / 2.0; >- double borderWidth = Math.min(adjustedWidth, adjustedHeight) / 10.0; // use 10% for the border - 5% on each side >- return new DisplayIndependentRectangle(adjustedX + borderWidth / 2.0, adjustedY + borderWidth / 2.0, adjustedWidth - borderWidth, adjustedHeight - borderWidth); >- } >- } >- >- /** >- * Find and set the node size - shift the nodes to the right and down to make >- * room for the width and height. >- * @param entitiesToLayout >- * @param relationships >- */ >- private void resizeAndShiftNodes(InternalNode[] entitiesToLayout) { >- // get maximum node size as percent of screen dimmensions >- double nodeSize = getNodeSize(entitiesToLayout); >- double halfNodeSize = nodeSize / 2; >- >- // Resize and shift nodes >- for (int i = 0; i < entitiesToLayout.length; i++) { >- InternalNode node = entitiesToLayout[i]; >- node.setInternalSize(nodeSize, nodeSize); >- node.setInternalLocation(node.getInternalX() + halfNodeSize, node.getInternalY() + halfNodeSize); >- } >- } >- >- /** >- * Convert all node positions into a percentage of the screen. If includeNodeSize >- * is true then this also updates the node's internal size. >- * @param entitiesToLayout >- */ >- private void convertPositionsToPercentage(InternalNode[] entitiesToLayout, InternalRelationship[] relationships, DisplayIndependentRectangle layoutBounds, boolean includeNodeSize) { >- >- // Adjust node positions and sizes >- for (int i = 0; i < entitiesToLayout.length; i++) { >- InternalNode node = entitiesToLayout[i]; >- DisplayIndependentPoint location = node.getInternalLocation().convertToPercent(layoutBounds); >- node.setInternalLocation(location.x, location.y); >- if (includeNodeSize) { // adjust node sizes >- double width = node.getInternalWidth() / layoutBounds.width; >- double height = node.getInternalHeight() / layoutBounds.height; >- node.setInternalSize(width, height); >- } >- } >- >- // Adjust bendpoint positions >- for (int i = 0; i < relationships.length; i++) { >- InternalRelationship rel = relationships[i]; >- for (int j = 0; j < rel.getBendPoints().size(); j++) { >- BendPoint bp = (BendPoint) rel.getBendPoints().get(j); >- DisplayIndependentPoint toPercent = bp.convertToPercent(layoutBounds); >- bp.setX(toPercent.x); >- bp.setY(toPercent.y); >- } >- } >- } >- >- /** >- * Convert the positions from a percentage of bounds area to fixed >- * coordinates. NOTE: ALL OF THE POSITIONS OF NODES UNTIL NOW WERE FOR THE >- * CENTER OF THE NODE - Convert it to the left top corner. >- * >- * @param entitiesToLayout >- * @param relationships >- * @param realBounds >- */ >- private void convertPositionsToCoords(InternalNode[] entitiesToLayout, InternalRelationship[] relationships, DisplayIndependentRectangle screenBounds) { >- >- // Adjust node positions and sizes >- for (int i = 0; i < entitiesToLayout.length; i++) { >- InternalNode node = entitiesToLayout[i]; >- double width = node.getInternalWidth() * screenBounds.width; >- double height = node.getInternalHeight() * screenBounds.height; >- DisplayIndependentPoint location = node.getInternalLocation().convertFromPercent(screenBounds); >- node.setInternalLocation(location.x - width / 2, location.y - height / 2); >- if (resizeEntitiesAfterLayout) { >- adjustNodeSizeAndPos(node, height, width); >- } else { >- node.setInternalSize(width, height); >- } >- } >- >- // Adjust bendpoint positions and shift based on source node size >- for (int i = 0; i < relationships.length; i++) { >- InternalRelationship rel = relationships[i]; >- for (int j = 0; j < rel.getBendPoints().size(); j++) { >- BendPoint bp = (BendPoint) rel.getBendPoints().get(j); >- DisplayIndependentPoint fromPercent = bp.convertFromPercent(screenBounds); >- bp.setX(fromPercent.x); >- bp.setY(fromPercent.y); >- } >- } >- } >- >- /** >- * Adjust node size to take advantage of space. Reset position to top left corner of node. >- * @param node >- * @param height >- * @param width >- */ >- private void adjustNodeSizeAndPos(InternalNode node, double height, double width) { >- double widthUsingHeight = height * widthToHeightRatio; >- if (widthToHeightRatio <= 1.0 && widthUsingHeight <= width) { >- double widthToUse = height * widthToHeightRatio; >- double leftOut = width - widthToUse; >- node.setInternalSize(Math.max(height * widthToHeightRatio, MIN_ENTITY_SIZE), Math.max(height, MIN_ENTITY_SIZE)); >- node.setInternalLocation(node.getInternalX() + leftOut / 2, node.getInternalY()); >- >- } else { >- double heightToUse = height / widthToHeightRatio; >- double leftOut = height - heightToUse; >- >- node.setInternalSize(Math.max(width, MIN_ENTITY_SIZE), Math.max(width / widthToHeightRatio, MIN_ENTITY_SIZE)); >- node.setInternalLocation(node.getInternalX(), node.getInternalY() + leftOut / 2); >- } >- >- } >- >- /** >- * Returns the maximum possible node size as a percentage of the width or height in current coord system. >- */ >- private double getNodeSize(InternalNode[] entitiesToLayout) { >- double width, height; >- if (entitiesToLayout.length == 1) { >- width = 0.8; >- height = 0.8; >- } else { >- DisplayIndependentDimension minimumDistance = getMinimumDistance(entitiesToLayout); >- width = 0.8 * minimumDistance.width; >- height = 0.8 * minimumDistance.height; >- } >- return Math.max(width, height); >- } >- >- /** >- * Find the bounds in which the nodes are located. Using the bounds against the real bounds >- * of the screen, the nodes can proportionally be placed within the real bounds. >- * The bounds can be determined either including the size of the nodes or not. If the size >- * is not included, the bounds will only be guaranteed to include the center of each node. >- */ >- protected DisplayIndependentRectangle getLayoutBounds(InternalNode[] entitiesToLayout, boolean includeNodeSize) { >- double rightSide = Double.MIN_VALUE; >- double bottomSide = Double.MIN_VALUE; >- double leftSide = Double.MAX_VALUE; >- double topSide = Double.MAX_VALUE; >- for (int i = 0; i < entitiesToLayout.length; i++) { >- InternalNode entity = entitiesToLayout[i]; >- if (entity.hasPreferredLocation()) { >- continue; >- } >- >- if (includeNodeSize) { >- leftSide = Math.min(entity.getInternalX() - entity.getInternalWidth() / 2, leftSide); >- topSide = Math.min(entity.getInternalY() - entity.getInternalHeight() / 2, topSide); >- rightSide = Math.max(entity.getInternalX() + entity.getInternalWidth() / 2, rightSide); >- bottomSide = Math.max(entity.getInternalY() + entity.getInternalHeight() / 2, bottomSide); >- } else { >- leftSide = Math.min(entity.getInternalX(), leftSide); >- topSide = Math.min(entity.getInternalY(), topSide); >- rightSide = Math.max(entity.getInternalX(), rightSide); >- bottomSide = Math.max(entity.getInternalY(), bottomSide); >- } >- } >- return new DisplayIndependentRectangle(leftSide, topSide, rightSide - leftSide, bottomSide - topSide); >- } >- >- /** >- * minDistance is the closest that any two points are together. >- * These two points become the center points for the two closest nodes, >- * which we wish to make them as big as possible without overlapping. >- * This will be the maximum of minDistanceX and minDistanceY minus a bit, lets say 20% >- * >- * We make the recommended node size a square for convenience. >- * >- * >- * _______ >- * | | >- * | | >- * | + | >- * | |\ | >- * |___|_\_|_____ >- * | | \ | >- * | | \ | >- * +-|---+ | >- * | | >- * |_______| >- * >- * >- * >- */ >- private DisplayIndependentDimension getMinimumDistance(InternalNode[] entitiesToLayout) { >- DisplayIndependentDimension horAndVertdistance = new DisplayIndependentDimension(Double.MAX_VALUE, Double.MAX_VALUE); >- double minDistance = Double.MAX_VALUE; // the minimum distance between all the nodes >- //TODO: Very Slow! >- for (int i = 0; i < entitiesToLayout.length; i++) { >- InternalNode layoutEntity1 = entitiesToLayout[i]; >- double x1 = layoutEntity1.getInternalX(); >- double y1 = layoutEntity1.getInternalY(); >- for (int j = i + 1; j < entitiesToLayout.length; j++) { >- InternalNode layoutEntity2 = entitiesToLayout[j]; >- double x2 = layoutEntity2.getInternalX(); >- double y2 = layoutEntity2.getInternalY(); >- double distanceX = Math.abs(x1 - x2); >- double distanceY = Math.abs(y1 - y2); >- double distance = Math.sqrt(Math.pow(distanceX, 2) + Math.pow(distanceY, 2)); >- >- if (distance < minDistance) { >- minDistance = distance; >- horAndVertdistance.width = distanceX; >- horAndVertdistance.height = distanceY; >- } >- } >- } >- return horAndVertdistance; >- } >- >- /** >- * Set the width to height ratio you want the entities to use >- */ >- public void setEntityAspectRatio(double ratio) { >- widthToHeightRatio = ratio; >- } >- >- /** >- * Returns the width to height ratio this layout will use to set the size of the entities. >- */ >- public double getEntityAspectRatio() { >- return widthToHeightRatio; >- } >- >- /** >- * A layout algorithm could take an uncomfortable amout of time to complete. To relieve some of >- * the mystery, the layout algorithm will notify each ProgressListener of its progress. >- */ >- public void addProgressListener(ProgressListener listener) { >- if (!progressListeners.contains(listener)) { >- progressListeners.add(listener); >- } >- } >- >- /** >- * Removes the given progress listener, preventing it from receiving any more updates. >- */ >- public void removeProgressListener(ProgressListener listener) { >- if (progressListeners.contains(listener)) { >- progressListeners.remove(listener); >- } >- } >- >- /** >- * Updates the layout locations so the external nodes know about the new locations >- */ >- protected void updateLayoutLocations(InternalNode[] nodes) { >- for (int i = 0; i < nodes.length; i++) { >- InternalNode node = nodes[i]; >- if (!node.hasPreferredLocation()) { >- node.setLocation(node.getInternalX(), node.getInternalY()); >- >- if ((layout_styles & LayoutStyles.NO_LAYOUT_NODE_RESIZING) != 1) { >- // Only set the size if we are supposed to >- node.setSize(node.getInternalWidth(), node.getInternalHeight()); >- } >- } >- } >- } >- >- protected void fireProgressStarted(int totalNumberOfSteps) { >- ProgressEvent event = new ProgressEvent(0, totalNumberOfSteps); >- for (int i = 0; i < progressListeners.size(); i++) { >- ProgressListener listener = (ProgressListener) progressListeners.get(i); >- >- listener.progressStarted(event); >- } >- } >- >- protected void fireProgressEnded(int totalNumberOfSteps) { >- ProgressEvent event = new ProgressEvent(totalNumberOfSteps, totalNumberOfSteps); >- for (int i = 0; i < progressListeners.size(); i++) { >- ProgressListener listener = (ProgressListener) progressListeners.get(i); >- listener.progressEnded(event); >- } >- >- } >- >- /** >- * Fires an event to notify all of the registered ProgressListeners that another step >- * has been completed in the algorithm. >- * @param currentStep The current step completed. >- * @param totalNumberOfSteps The total number of steps in the algorithm. >- */ >- protected void fireProgressEvent(int currentStep, int totalNumberOfSteps) { >- >- // Update the layout locations to the external nodes >- Calendar now = Calendar.getInstance(); >- now.add(Calendar.MILLISECOND, -MIN_TIME_DELAY_BETWEEN_PROGRESS_EVENTS); >- >- if (now.after(lastProgressEventFired) || currentStep == totalNumberOfSteps) { >- ProgressEvent event = new ProgressEvent(currentStep, totalNumberOfSteps); >- >- for (int i = 0; i < progressListeners.size(); i++) { >- >- ProgressListener listener = (ProgressListener) progressListeners.get(i); >- listener.progressUpdated(event); >- } >- lastProgressEventFired = Calendar.getInstance(); >- } >- >- } >- >- protected int getNumberOfProgressListeners() { >- return progressListeners.size(); >- } >- >- private void checkThread() { >- if (this.creationThread != Thread.currentThread()) { >- throw new RuntimeException("Invalid Thread Access."); >- } >- } >- >-} >Index: src/org/eclipse/zest/layouts/algorithms/AlgorithmHelper.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/algorithms/AlgorithmHelper.java >diff -N src/org/eclipse/zest/layouts/algorithms/AlgorithmHelper.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/zest/layouts/algorithms/AlgorithmHelper.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,205 @@ >+/******************************************************************************* >+ * Copyright (c) 2005-2010 The Chisel Group and others. 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 - initial API and implementation >+ * Mateusz Matela >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.layouts.algorithms; >+ >+import org.eclipse.zest.layouts.dataStructures.DisplayIndependentDimension; >+import org.eclipse.zest.layouts.dataStructures.DisplayIndependentPoint; >+import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle; >+import org.eclipse.zest.layouts.interfaces.EntityLayout; >+ >+class AlgorithmHelper { >+ >+ public static int MIN_NODE_SIZE = 8; >+ >+ public static double PADDING_PERCENT = 0.8; >+ >+ /** >+ * Fits given entities within given bounds, preserving their relative >+ * locations. >+ * >+ * @param entities >+ * @param destinationBounds >+ * @param resize >+ */ >+ public static void fitWithinBounds(EntityLayout[] entities, DisplayIndependentRectangle destinationBounds, boolean resize) { >+ DisplayIndependentRectangle startingBounds = getLayoutBounds(entities, false); >+ double sizeScale = Math.min(destinationBounds.width / startingBounds.width, destinationBounds.height / startingBounds.height); >+ if (entities.length == 1) { >+ fitSingleEntity(entities[0], destinationBounds, resize); >+ return; >+ } >+ for (int i = 0; i < entities.length; i++) { >+ EntityLayout entity = entities[i]; >+ DisplayIndependentDimension size = entity.getSize(); >+ if (entity.isMovable()) { >+ DisplayIndependentPoint location = entity.getLocation(); >+ double percentX = (location.x - startingBounds.x) / (startingBounds.width); >+ double percentY = (location.y - startingBounds.y) / (startingBounds.height); >+ >+ if (resize && entity.isResizable()) { >+ size.width *= sizeScale; >+ size.height *= sizeScale; >+ entity.setSize(size.width, size.height); >+ } >+ >+ location.x = destinationBounds.x + size.width / 2 + percentX * (destinationBounds.width - size.width); >+ location.y = destinationBounds.y + size.height / 2 + percentY * (destinationBounds.height - size.height); >+ entity.setLocation(location.x, location.y); >+ >+ } else >+ if (resize && entity.isResizable()) { >+ entity.setSize(size.width * sizeScale, size.height * sizeScale); >+ } >+ } >+ } >+ >+ private static void fitSingleEntity(EntityLayout entity, DisplayIndependentRectangle destinationBounds, boolean resize) { >+ if (entity.isMovable()) { >+ entity.setLocation(destinationBounds.x + destinationBounds.width / 2, destinationBounds.y + destinationBounds.height / 2); >+ } >+ if (resize && entity.isResizable()) { >+ double width = destinationBounds.width; >+ double height = destinationBounds.height; >+ double preferredAspectRatio = entity.getPreferredAspectRatio(); >+ if (preferredAspectRatio > 0) { >+ DisplayIndependentDimension fixedSize = fixAspectRatio(width, height, preferredAspectRatio); >+ entity.setSize(fixedSize.width, fixedSize.height); >+ } else { >+ entity.setSize(width, height); >+ } >+ } >+ } >+ >+ /** >+ * Resizes the nodes so that they have a maximal area without overlapping >+ * each other, with additional empty space of 20% of node's width (or >+ * height, if bigger). It does nothing if there's less than two nodes. >+ * >+ * @param entities >+ */ >+ public static void maximizeSizes(EntityLayout[] entities) { >+ if (entities.length > 1) { >+ DisplayIndependentDimension minDistance = getMinimumDistance(entities); >+ double nodeSize = Math.max(minDistance.width, minDistance.height) * PADDING_PERCENT; >+ double width = nodeSize; >+ double height = nodeSize; >+ for (int i = 0; i < entities.length; i++) { >+ EntityLayout entity = entities[i]; >+ if (entity.isResizable()) { >+ double preferredRatio = entity.getPreferredAspectRatio(); >+ if (preferredRatio > 0) { >+ DisplayIndependentDimension fixedSize = fixAspectRatio(width, height, preferredRatio); >+ entity.setSize(fixedSize.width, fixedSize.height); >+ } else { >+ entity.setSize(width, height); >+ } >+ } >+ } >+ } >+ } >+ >+ private static DisplayIndependentDimension fixAspectRatio(double width, double height, double preferredRatio) { >+ double actualRatio = width / height; >+ if (actualRatio > preferredRatio) { >+ width = height * preferredRatio; >+ if (width < MIN_NODE_SIZE) { >+ width = MIN_NODE_SIZE; >+ height = width / preferredRatio; >+ } >+ } >+ if (actualRatio < preferredRatio) { >+ height = width / preferredRatio; >+ if (height < MIN_NODE_SIZE) { >+ height = MIN_NODE_SIZE; >+ width = height * preferredRatio; >+ } >+ } >+ return new DisplayIndependentDimension(width, height); >+ } >+ >+ /** >+ * Find the bounds in which the nodes are located. Using the bounds against >+ * the real bounds of the screen, the nodes can proportionally be placed >+ * within the real bounds. The bounds can be determined either including the >+ * size of the nodes or not. If the size is not included, the bounds will >+ * only be guaranteed to include the center of each node. >+ */ >+ public static DisplayIndependentRectangle getLayoutBounds(EntityLayout[] entities, boolean includeNodeSize) { >+ double rightSide = Double.NEGATIVE_INFINITY; >+ double bottomSide = Double.NEGATIVE_INFINITY; >+ double leftSide = Double.POSITIVE_INFINITY; >+ double topSide = Double.POSITIVE_INFINITY; >+ for (int i = 0; i < entities.length; i++) { >+ EntityLayout entity = entities[i]; >+ DisplayIndependentPoint location = entity.getLocation(); >+ DisplayIndependentDimension size = entity.getSize(); >+ if (includeNodeSize) { >+ leftSide = Math.min(location.x - size.width / 2, leftSide); >+ topSide = Math.min(location.y - size.height / 2, topSide); >+ rightSide = Math.max(location.x + size.width / 2, rightSide); >+ bottomSide = Math.max(location.y + size.height / 2, bottomSide); >+ } else { >+ leftSide = Math.min(location.x, leftSide); >+ topSide = Math.min(location.y, topSide); >+ rightSide = Math.max(location.x, rightSide); >+ bottomSide = Math.max(location.y, bottomSide); >+ } >+ } >+ return new DisplayIndependentRectangle(leftSide, topSide, rightSide - leftSide, bottomSide - topSide); >+ } >+ >+ /** >+ * minDistance is the closest that any two points are together. These two >+ * points become the center points for the two closest nodes, which we wish >+ * to make them as big as possible without overlapping. This will be the >+ * maximum of minDistanceX and minDistanceY minus a bit, lets say 20% >+ * >+ * We make the recommended node size a square for convenience. >+ * >+ * <pre> >+ * _______ >+ * | | >+ * | | >+ * | + | >+ * | |\ | >+ * |___|_\_|_____ >+ * | | \ | >+ * | | \ | >+ * +-|---+ | >+ * | | >+ * |_______| >+ * </pre> >+ * >+ * >+ */ >+ public static DisplayIndependentDimension getMinimumDistance(EntityLayout[] entities) { >+ DisplayIndependentDimension horAndVertdistance = new DisplayIndependentDimension(Double.MAX_VALUE, Double.MAX_VALUE); >+ double minDistance = Double.MAX_VALUE; >+ >+ // TODO: Very Slow! >+ for (int i = 0; i < entities.length; i++) { >+ DisplayIndependentPoint location1 = entities[i].getLocation(); >+ for (int j = i + 1; j < entities.length; j++) { >+ DisplayIndependentPoint location2 = entities[j].getLocation(); >+ double distanceX = location1.x - location2.x; >+ double distanceY = location1.y - location2.y; >+ double distance = distanceX * distanceX + distanceY * distanceY; >+ >+ if (distance < minDistance) { >+ minDistance = distance; >+ horAndVertdistance.width = Math.abs(distanceX); >+ horAndVertdistance.height = Math.abs(distanceY); >+ } >+ } >+ } >+ return horAndVertdistance; >+ } >+} >Index: src/org/eclipse/zest/layouts/algorithms/BoxLayoutAlgorithm.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/algorithms/BoxLayoutAlgorithm.java >diff -N src/org/eclipse/zest/layouts/algorithms/BoxLayoutAlgorithm.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/zest/layouts/algorithms/BoxLayoutAlgorithm.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,48 @@ >+/******************************************************************************* >+ * Copyright (c) 2009-2010 Mateusz Matela and others. 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: Mateusz Matela - initial API and implementation >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.layouts.algorithms; >+ >+/** >+ * Layout algorithm that places all elements in one column or one row, depending >+ * on set orientation. >+ */ >+public class BoxLayoutAlgorithm extends GridLayoutAlgorithm { >+ >+ public static final int HORIZONTAL = 1; >+ >+ public static final int VERTICAL = 2; >+ >+ private int orientation = HORIZONTAL; >+ >+ public BoxLayoutAlgorithm() { >+ } >+ >+ public BoxLayoutAlgorithm(int orientation) { >+ setOrientation(orientation); >+ } >+ >+ public int getOrientation() { >+ return orientation; >+ } >+ >+ public void setOrientation(int orientation) { >+ if (orientation == HORIZONTAL || orientation == VERTICAL) >+ this.orientation = orientation; >+ else >+ throw new RuntimeException("Invalid orientation: " + orientation); >+ } >+ >+ protected int[] calculateNumberOfRowsAndCols(int numChildren, double boundX, double boundY, double boundWidth, double boundHeight) { >+ if (orientation == HORIZONTAL) >+ return new int[] { numChildren, 1 }; >+ else >+ return new int[] { 1, numChildren }; >+ } >+} >Index: src/org/eclipse/zest/layouts/algorithms/CompositeLayoutAlgorithm.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.layouts/src/org/eclipse/zest/layouts/algorithms/CompositeLayoutAlgorithm.java,v >retrieving revision 1.5 >diff -u -r1.5 CompositeLayoutAlgorithm.java >--- src/org/eclipse/zest/layouts/algorithms/CompositeLayoutAlgorithm.java 31 Mar 2009 16:42:20 -0000 1.5 >+++ src/org/eclipse/zest/layouts/algorithms/CompositeLayoutAlgorithm.java 15 Mar 2010 03:49:05 -0000 >@@ -1,77 +1,35 @@ >-/******************************************************************************* >- * Copyright 2006, 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.layouts.algorithms; >- >-import org.eclipse.zest.layouts.InvalidLayoutConfiguration; >-import org.eclipse.zest.layouts.LayoutAlgorithm; >-import org.eclipse.zest.layouts.dataStructures.InternalNode; >-import org.eclipse.zest.layouts.dataStructures.InternalRelationship; >- >-public class CompositeLayoutAlgorithm extends AbstractLayoutAlgorithm { >- >- LayoutAlgorithm[] algorithms = null; >- >- public CompositeLayoutAlgorithm(int styles, LayoutAlgorithm[] algoirthms) { >- super(styles); >- this.algorithms = algoirthms; >- } >- >- public CompositeLayoutAlgorithm( LayoutAlgorithm[] algoirthms) { >- this(0, algoirthms); >- } >- >- protected void applyLayoutInternal(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double boundsX, double boundsY, double boundsWidth, double boundsHeight) { >- >- for (int i = 0; i < algorithms.length; i++) { >- try { >- algorithms[i].applyLayout(entitiesToLayout, relationshipsToConsider, boundsX, boundsY, boundsWidth, boundsHeight, this.internalAsynchronous, this.internalContinuous); >- } catch (InvalidLayoutConfiguration e) { >- e.printStackTrace(); >- } >- } >- for (int i = 0; i < entitiesToLayout.length; i++) { >- entitiesToLayout[i].getLayoutEntity().setLocationInLayout(entitiesToLayout[i].getXInLayout(), entitiesToLayout[i].getYInLayout()); >- } >- >- //updateLayoutLocations(entitiesToLayout); >- } >- >- protected int getCurrentLayoutStep() { >- // TODO Auto-generated method stub >- return 0; >- } >- >- protected int getTotalNumberOfLayoutSteps() { >- // TODO Auto-generated method stub >- return 0; >- } >- >- protected boolean isValidConfiguration(boolean asynchronous, boolean continuous) { >- // TODO Auto-generated method stub >- return true; >- } >- >- protected void postLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider) { >- // TODO Auto-generated method stub >- >- } >- >- protected void preLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height) { >- // TODO Auto-generated method stub >- >- } >- >- public void setLayoutArea(double x, double y, double width, double height) { >- // TODO Auto-generated method stub >- >- } >- >-} >+/******************************************************************************* >+ * Copyright (c) 2005-2010 The Chisel Group and others. 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 - initial API and implementation >+ * Mateusz Matela >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.layouts.algorithms; >+ >+import org.eclipse.zest.layouts.LayoutAlgorithm; >+import org.eclipse.zest.layouts.interfaces.LayoutContext; >+ >+public class CompositeLayoutAlgorithm implements LayoutAlgorithm { >+ >+ private LayoutAlgorithm[] algorithms = null; >+ >+ public CompositeLayoutAlgorithm(LayoutAlgorithm[] algorithms) { >+ this.algorithms = algorithms; >+ } >+ >+ public void applyLayout(boolean clean) { >+ for (int i = 0; i < algorithms.length; i++) { >+ algorithms[i].applyLayout(clean); >+ } >+ } >+ >+ public void setLayoutContext(LayoutContext context) { >+ for (int i = 0; i < algorithms.length; i++) { >+ algorithms[i].setLayoutContext(context); >+ } >+ } >+} >Index: src/org/eclipse/zest/layouts/algorithms/ContinuousLayoutAlgorithm.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/algorithms/ContinuousLayoutAlgorithm.java >diff -N src/org/eclipse/zest/layouts/algorithms/ContinuousLayoutAlgorithm.java >--- src/org/eclipse/zest/layouts/algorithms/ContinuousLayoutAlgorithm.java 12 Sep 2007 20:44:37 -0000 1.10 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,102 +0,0 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts.algorithms; >- >-import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle; >-import org.eclipse.zest.layouts.dataStructures.InternalNode; >-import org.eclipse.zest.layouts.dataStructures.InternalRelationship; >- >-/** >- * >- * @author Ian Bull >- * >- * Used to represent algorithms that can continuously run. >- * >- */ >-public abstract class ContinuousLayoutAlgorithm extends AbstractLayoutAlgorithm { >- >- double x, y, widht, height; >- >- public ContinuousLayoutAlgorithm(int styles) { >- super(styles); >- } >- >- /** >- * The logic to determine if a layout should continue running or not >- */ >- protected abstract boolean performAnotherNonContinuousIteration(); >- >- /** >- * Computes a single iteration of the layout algorithm >- * @return >- */ >- protected abstract void computeOneIteration(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height); >- >- private boolean continueRunning() { >- if (layoutStopped) { >- return false; >- } else if (this.internalContinuous && !layoutStopped) { >- return true; >- } else if (performAnotherNonContinuousIteration()) { >- return true; >- } else { >- return false; >- } >- } >- >- public void setLayoutArea(double x, double y, double width, double height) { >- this.setBounds(x, y, width, height); >- >- } >- >- public synchronized DisplayIndependentRectangle getBounds() { >- return new DisplayIndependentRectangle(this.x, this.y, this.widht, this.height); >- } >- >- public synchronized void setBounds(double x, double y, double width, double height) { >- this.x = x; >- this.y = y; >- this.widht = width; >- this.height = height; >- } >- >- /** >- * Calculates and applies the positions of the given entities based on a >- * spring layout using the given relationships. >- */ >- protected void applyLayoutInternal(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height) { >- >- this.setBounds(x, y, width, height); >- >- while (continueRunning()) { >- // check for entities and relationships to add or remove >- entitiesToLayout = updateEntities(entitiesToLayout); >- relationshipsToConsider = updateRelationships(relationshipsToConsider); >- DisplayIndependentRectangle bounds = this.getBounds(); >- double localX = bounds.x; >- double localY = bounds.y; >- double localWidth = bounds.width; >- double localHeight = bounds.height; >- >- computeOneIteration(entitiesToLayout, relationshipsToConsider, localX, localY, localWidth, localHeight); >- >- updateLayoutLocations(entitiesToLayout); >- >- if (this.internalContinuous) { >- fireProgressEvent(1, 1); >- } else { >- fireProgressEvent(getCurrentLayoutStep(), getTotalNumberOfLayoutSteps()); >- } >- >- } >- } >- >-} >Index: src/org/eclipse/zest/layouts/algorithms/DirectedGraphLayoutAlgorithm.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.layouts/src/org/eclipse/zest/layouts/algorithms/DirectedGraphLayoutAlgorithm.java,v >retrieving revision 1.5 >diff -u -r1.5 DirectedGraphLayoutAlgorithm.java >--- src/org/eclipse/zest/layouts/algorithms/DirectedGraphLayoutAlgorithm.java 4 May 2009 05:47:36 -0000 1.5 >+++ src/org/eclipse/zest/layouts/algorithms/DirectedGraphLayoutAlgorithm.java 15 Mar 2010 03:49:05 -0000 >@@ -1,124 +1,132 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts.algorithms; >- >-import java.lang.reflect.Field; >-import java.util.HashMap; >-import java.util.Iterator; >-import java.util.List; >- >-import org.eclipse.draw2d.geometry.Dimension; >-import org.eclipse.draw2d.graph.DirectedGraph; >-import org.eclipse.draw2d.graph.DirectedGraphLayout; >-import org.eclipse.draw2d.graph.Edge; >-import org.eclipse.draw2d.graph.Node; >-import org.eclipse.swt.SWT; >-import org.eclipse.zest.layouts.dataStructures.InternalNode; >-import org.eclipse.zest.layouts.dataStructures.InternalRelationship; >- >-public class DirectedGraphLayoutAlgorithm extends AbstractLayoutAlgorithm { >- >- class ExtendedDirectedGraphLayout extends DirectedGraphLayout { >- >- public void visit(DirectedGraph graph) { >- Field field; >- try { >- field = DirectedGraphLayout.class.getDeclaredField("steps"); >- field.setAccessible(true); >- Object object = field.get(this); >- List steps = (List) object; >- steps.remove(10); >- steps.remove(9); >- steps.remove(8); >- steps.remove(2); >- field.setAccessible(false); >- super.visit(graph); >- } catch (SecurityException e) { >- e.printStackTrace(); >- } catch (NoSuchFieldException e) { >- e.printStackTrace(); >- } catch (IllegalArgumentException e) { >- e.printStackTrace(); >- } catch (IllegalAccessException e) { >- e.printStackTrace(); >- } >- } >- >- } >- >- public DirectedGraphLayoutAlgorithm(int styles) { >- super(styles); >- } >- >- protected void applyLayoutInternal(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double boundsX, double boundsY, double boundsWidth, double boundsHeight) { >- HashMap mapping = new HashMap(entitiesToLayout.length); >- DirectedGraph graph = new DirectedGraph(); >- for (int i = 0; i < entitiesToLayout.length; i++) { >- InternalNode internalNode = entitiesToLayout[i]; >- Node node = new Node(internalNode); >- node.setSize(new Dimension(10, 10)); >- mapping.put(internalNode, node); >- graph.nodes.add(node); >- } >- for (int i = 0; i < relationshipsToConsider.length; i++) { >- InternalRelationship relationship = relationshipsToConsider[i]; >- Node source = (Node) mapping.get(relationship.getSource()); >- Node dest = (Node) mapping.get(relationship.getDestination()); >- Edge edge = new Edge(relationship, source, dest); >- graph.edges.add(edge); >- } >- DirectedGraphLayout directedGraphLayout = new ExtendedDirectedGraphLayout(); >- directedGraphLayout.visit(graph); >- >- for (Iterator iterator = graph.nodes.iterator(); iterator.hasNext();) { >- Node node = (Node) iterator.next(); >- InternalNode internalNode = (InternalNode) node.data; >- // For horizontal layout transpose the x and y coordinates >- if ((layout_styles & SWT.HORIZONTAL) == SWT.HORIZONTAL) { >- internalNode.setInternalLocation(node.y, node.x); >- }else { >- internalNode.setInternalLocation(node.x, node.y); >- } >- } >- updateLayoutLocations(entitiesToLayout); >- } >- >- protected int getCurrentLayoutStep() { >- // TODO Auto-generated method stub >- return 0; >- } >- >- protected int getTotalNumberOfLayoutSteps() { >- // TODO Auto-generated method stub >- return 0; >- } >- >- protected boolean isValidConfiguration(boolean asynchronous, boolean continuous) { >- // TODO Auto-generated method stub >- return true; >- } >- >- protected void postLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider) { >- // TODO Auto-generated method stub >- >- } >- >- protected void preLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height) { >- // TODO Auto-generated method stub >- >- } >- >- public void setLayoutArea(double x, double y, double width, double height) { >- // TODO Auto-generated method stub >- >- } >- >-} >+/******************************************************************************* >+ * Copyright (c) 2005-2010 The Chisel Group and others. 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 - initial API and implementation >+ * Mateusz Matela >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.layouts.algorithms; >+ >+import java.lang.reflect.Field; >+import java.util.HashMap; >+import java.util.Iterator; >+import java.util.List; >+ >+import org.eclipse.draw2d.geometry.Dimension; >+import org.eclipse.draw2d.graph.DirectedGraph; >+import org.eclipse.draw2d.graph.DirectedGraphLayout; >+import org.eclipse.draw2d.graph.Edge; >+import org.eclipse.draw2d.graph.Node; >+import org.eclipse.zest.layouts.LayoutAlgorithm; >+import org.eclipse.zest.layouts.interfaces.ConnectionLayout; >+import org.eclipse.zest.layouts.interfaces.EntityLayout; >+import org.eclipse.zest.layouts.interfaces.LayoutContext; >+import org.eclipse.zest.layouts.interfaces.NodeLayout; >+import org.eclipse.zest.layouts.interfaces.SubgraphLayout; >+ >+public class DirectedGraphLayoutAlgorithm implements LayoutAlgorithm { >+ >+ class ExtendedDirectedGraphLayout extends DirectedGraphLayout { >+ >+ public void visit(DirectedGraph graph) { >+ Field field; >+ try { >+ field = DirectedGraphLayout.class.getDeclaredField("steps"); >+ field.setAccessible(true); >+ Object object = field.get(this); >+ List steps = (List) object; >+ // steps.remove(10); >+ // steps.remove(9); >+ // steps.remove(8); >+ // steps.remove(2); >+ field.setAccessible(false); >+ super.visit(graph); >+ } catch (SecurityException e) { >+ e.printStackTrace(); >+ } catch (NoSuchFieldException e) { >+ e.printStackTrace(); >+ } catch (IllegalArgumentException e) { >+ e.printStackTrace(); >+ } catch (IllegalAccessException e) { >+ e.printStackTrace(); >+ } >+ } >+ } >+ >+ public static final int HORIZONTAL = 1; >+ >+ public static final int VERTICAL = 2; >+ >+ private int orientation = VERTICAL; >+ >+ private LayoutContext context; >+ >+ public DirectedGraphLayoutAlgorithm() { >+ } >+ >+ public DirectedGraphLayoutAlgorithm(int orientation) { >+ if (orientation == VERTICAL) >+ this.orientation = orientation; >+ } >+ >+ public int getOrientation() { >+ return orientation; >+ } >+ >+ public void setOrientation(int orientation) { >+ if (orientation == HORIZONTAL || orientation == VERTICAL) >+ this.orientation = orientation; >+ } >+ >+ public void applyLayout(boolean clean) { >+ if (!clean) >+ return; >+ HashMap mapping = new HashMap(); >+ DirectedGraph graph = new DirectedGraph(); >+ EntityLayout[] entities = context.getEntities(); >+ for (int i = 0; i < entities.length; i++) { >+ Node node = new Node(entities[i]); >+ node.setSize(new Dimension(10, 10)); >+ mapping.put(entities[i], node); >+ graph.nodes.add(node); >+ } >+ ConnectionLayout[] connections = context.getConnections(); >+ for (int i = 0; i < connections.length; i++) { >+ Node source = (Node) mapping.get(getEntity(connections[i].getSource())); >+ Node dest = (Node) mapping.get(getEntity(connections[i].getTarget())); >+ if (source != null && dest != null) { >+ Edge edge = new Edge(connections[i], source, dest); >+ graph.edges.add(edge); >+ } >+ } >+ DirectedGraphLayout directedGraphLayout = new ExtendedDirectedGraphLayout(); >+ directedGraphLayout.visit(graph); >+ >+ for (Iterator iterator = graph.nodes.iterator(); iterator.hasNext();) { >+ Node node = (Node) iterator.next(); >+ EntityLayout entity = (EntityLayout) node.data; >+ if (orientation == VERTICAL) { >+ entity.setLocation(node.x, node.y); >+ } else { >+ entity.setLocation(node.y, node.x); >+ } >+ } >+ } >+ >+ private EntityLayout getEntity(NodeLayout node) { >+ if (!node.isPruned()) >+ return node; >+ SubgraphLayout subgraph = node.getSubgraph(); >+ if (subgraph.isGraphEntity()) >+ return subgraph; >+ return null; >+ } >+ >+ public void setLayoutContext(LayoutContext context) { >+ this.context = context; >+ } >+ >+} >Index: src/org/eclipse/zest/layouts/algorithms/GridLayoutAlgorithm.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.layouts/src/org/eclipse/zest/layouts/algorithms/GridLayoutAlgorithm.java,v >retrieving revision 1.7 >diff -u -r1.7 GridLayoutAlgorithm.java >--- src/org/eclipse/zest/layouts/algorithms/GridLayoutAlgorithm.java 12 Sep 2007 20:44:37 -0000 1.7 >+++ src/org/eclipse/zest/layouts/algorithms/GridLayoutAlgorithm.java 15 Mar 2010 03:49:05 -0000 >@@ -1,233 +1,222 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts.algorithms; >- >-import java.util.Arrays; >- >-import org.eclipse.zest.layouts.LayoutStyles; >-import org.eclipse.zest.layouts.dataStructures.InternalNode; >-import org.eclipse.zest.layouts.dataStructures.InternalRelationship; >- >- >-/** >- * @version 2.0 >- * @author Ian Bull >- * @author Casey Best and Rob Lintern >- */ >-public class GridLayoutAlgorithm extends AbstractLayoutAlgorithm { >- >- private static final double PADDING_PERCENTAGE = 0.95; >- >- protected int rowPadding = 0; >- >- public void setLayoutArea(double x, double y, double width, double height) { >- throw new RuntimeException("Operation not implemented"); >- } >- >- int rows, cols, numChildren; >- double colWidth, rowHeight, offsetX, offsetY; >- int totalProgress; >- double h, w; >- >- /** >- * Initializes the grid layout. >- * @param styles >- * @see LayoutStyles >- */ >- public GridLayoutAlgorithm(int styles) { >- super(styles); >- } >- >- /** >- * Inititalizes the grid layout with no style. >- */ >- public GridLayoutAlgorithm() { >- this(LayoutStyles.NONE); >- } >- >- >- protected int getCurrentLayoutStep() { >- // TODO: This isn't right >- return 0; >- } >- >- protected int getTotalNumberOfLayoutSteps() { >- return totalProgress; >- } >- >- /** >- * >- */ >- protected void preLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height) { >- >- // TODO: Filter unwanted entities and relationships >- //super.applyLayout (entitiesToLayout, relationshipsToConsider, boundsX, boundsY, boundsWidth, boundsHeight); >- // now begin >- numChildren = entitiesToLayout.length; >- if (numChildren < 1) return; >- >- int[] colsAndRows = calculateNumberOfRowsAndCols(numChildren, x, y, width, height); >- cols = colsAndRows[0]; >- rows = colsAndRows[1]; >- >- totalProgress = rows + 2; >- fireProgressEvent (1, totalProgress); >- >- // sort the entities >- if (comparator != null) { >- Arrays.sort(entitiesToLayout, comparator); >- } else { >- Arrays.sort(entitiesToLayout); >- } >- fireProgressEvent (2, totalProgress); >- >- // Calculate row height and column width >- colWidth = width/cols; >- rowHeight = height/rows; >- >- // Calculate amount to scale children >- double [] nodeSize = calculateNodeSize (colWidth, rowHeight); >- w = nodeSize[0]; >- h = nodeSize[1]; >- offsetX = (colWidth - w)/2.0; // half of the space between columns >- offsetY = (rowHeight - h)/2.0; // half of the space between rows >- } >- >- /** >- * Use this algorithm to layout the given entities, using the given relationships and bounds. >- * The entities will be placed in the same order as they are passed in, unless a comparator >- * is supplied. >- * >- * @param entitiesToLayout Apply the algorithm to these entities >- * @param relationshipsToConsider Only consider these relationships when applying the algorithm. >- * @param boundsX The left side of the bounds in which the layout can place the entities. >- * @param boundsY The top side of the bounds in which the layout can place the entities. >- * @param boundsWidth The width of the bounds in which the layout can place the entities. >- * @param boundsHeight The height of the bounds in which the layout can place the entities. >- * @throws RuntimeException Thrown if entitiesToLayout doesn't contain all of the endpoints for each relationship in relationshipsToConsider >- */ >- protected synchronized void applyLayoutInternal(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double boundsX, double boundsY, double boundsWidth, double boundsHeight) { >- >- int index = 0; >- for( int i = 0; i < rows; i++ ) { >- for( int j = 0; j < cols; j++ ) { >- if( (i*cols + j) < numChildren ) { >- // find new position for child >- double xmove = boundsX + j * colWidth + offsetX; >- double ymove = boundsY + i * rowHeight + offsetY; >- InternalNode sn = entitiesToLayout[index++]; >- sn.setInternalLocation( xmove, ymove ); >- sn.setInternalSize( Math.max(w, MIN_ENTITY_SIZE), Math.max(h, MIN_ENTITY_SIZE) ); >- } >- } >- fireProgressEvent (2 + i, totalProgress); >- } >- updateLayoutLocations(entitiesToLayout); >- fireProgressEvent (totalProgress, totalProgress); >- } >- >- protected void postLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider) { >- >- } >- >- /** >- * Calculates and returns an array containing the number of columns, followed by the number of rows >- */ >- protected int[] calculateNumberOfRowsAndCols (int numChildren, double boundX, double boundY, double boundWidth, double boundHeight) { >- if (getEntityAspectRatio() == 1.0) { >- return calculateNumberOfRowsAndCols_square (numChildren, boundX, boundY, boundWidth, boundHeight); >- } else { >- return calculateNumberOfRowsAndCols_rectangular (numChildren); >- } >- } >- >- protected int[] calculateNumberOfRowsAndCols_square (int numChildren, double boundX, double boundY, double boundWidth, double boundHeight) { >- int rows = Math.max (1, (int) Math.sqrt(numChildren * boundHeight/boundWidth)); >- int cols = Math.max (1, (int) Math.sqrt(numChildren * boundWidth/boundHeight)); >- >- // if space is taller than wide, adjust rows first >- if (boundWidth <= boundHeight) { >- //decrease number of rows and columns until just enough or not enough >- while (rows*cols > numChildren) { >- if (rows > 1) >- rows--; >- if (rows*cols > numChildren) >- if (cols > 1) >- cols--; >- } >- //increase number of rows and columns until just enough >- while (rows*cols < numChildren) { >- rows++; >- if (rows*cols < numChildren) >- cols++; >- } >- } else { >- //decrease number of rows and columns until just enough or not enough >- while (rows*cols > numChildren) { >- if (cols > 1) >- cols--; >- if (rows*cols > numChildren) >- if (rows > 1) >- rows--; >- } >- //increase number of rows and columns until just enough >- while (rows*cols < numChildren) { >- cols++; >- if (rows*cols < numChildren) >- rows++; >- } >- } >- int[] result = {cols, rows}; >- return result; >- } >- >- protected int [] calculateNumberOfRowsAndCols_rectangular (int numChildren) { >- int rows = Math.max (1, (int)Math.ceil(Math.sqrt(numChildren))); >- int cols = Math.max (1, (int)Math.ceil(Math.sqrt(numChildren))); >- int[] result = {cols, rows}; >- return result; >- } >- >- >- protected double [] calculateNodeSize (double colWidth, double rowHeight) { >- double childW = Math.max (MIN_ENTITY_SIZE, PADDING_PERCENTAGE*colWidth); >- double childH = Math.max (MIN_ENTITY_SIZE, PADDING_PERCENTAGE*(rowHeight - rowPadding)); >- double whRatio = colWidth/rowHeight; >- if (whRatio < getEntityAspectRatio()) { >- childH = childW/getEntityAspectRatio(); >- } else { >- childW = childH*getEntityAspectRatio(); >- } >- double [] result = {childW, childH}; >- return result; >- } >- >- /** >- * Increases the padding between rows in the grid >- * @param rowPadding Value will not be set if less than 0. >- */ >- public void setRowPadding(int rowPadding) { >- if (rowPadding < 0 ) { >- return; >- } >- this.rowPadding = rowPadding; >- } >- >- protected boolean isValidConfiguration(boolean asynchronous, boolean continueous) { >- if ( asynchronous && continueous ) return false; >- else if ( asynchronous && !continueous ) return true; >- else if ( !asynchronous && continueous ) return false; >- else if ( !asynchronous && !continueous ) return true; >- >- return false; >- } >- >-} >+/******************************************************************************* >+ * Copyright (c) 2005-2010 The Chisel Group and others. 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 - initial API and implementation >+ * Mateusz Matela >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.layouts.algorithms; >+ >+import org.eclipse.zest.layouts.LayoutAlgorithm; >+import org.eclipse.zest.layouts.dataStructures.DisplayIndependentDimension; >+import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle; >+import org.eclipse.zest.layouts.interfaces.EntityLayout; >+import org.eclipse.zest.layouts.interfaces.LayoutContext; >+ >+ >+/** >+ * @version 2.0 >+ * @author Ian Bull >+ * @author Casey Best and Rob Lintern >+ */ >+public class GridLayoutAlgorithm implements LayoutAlgorithm { >+ >+ private static final double PADDING_PERCENTAGE = 0.95; >+ private static final int MIN_ENTITY_SIZE = 5; >+ >+ protected double aspectRatio = 1.0; >+ protected int rowPadding = 0; >+ private boolean resize = false; >+ protected int rows, cols, numChildren; >+ protected double colWidth, rowHeight, offsetX, offsetY; >+ protected double childrenHeight, childrenWidth; >+ >+ private LayoutContext context; >+ >+ >+ public void setLayoutContext(LayoutContext context) { >+ this.context = context; >+ } >+ >+ public void applyLayout(boolean clean) { >+ if (!clean) >+ return; >+ DisplayIndependentRectangle bounds = context.getBounds(); >+ calculateGrid(bounds); >+ applyLayoutInternal(context.getEntities(), bounds); >+ } >+ >+ /** >+ * Calculates all the dimensions of grid that layout entities will be fit >+ * in. The following fields are set by this method: {@link #numChildren}, >+ * {@link #rows}, {@link #cols}, {@link #colWidth}, {@link #rowHeight}, >+ * {@link #offsetX}, {@link #offsetY} >+ * >+ * @param bounds >+ */ >+ protected void calculateGrid(DisplayIndependentRectangle bounds) { >+ numChildren = context.getNodes().length; >+ int[] result = calculateNumberOfRowsAndCols(numChildren, bounds.x, bounds.y, bounds.width, bounds.height); >+ cols = result[0]; >+ rows = result[1]; >+ >+ colWidth = bounds.width / cols; >+ rowHeight = bounds.height / rows; >+ >+ double [] nodeSize = calculateNodeSize (colWidth, rowHeight); >+ childrenWidth = nodeSize[0]; >+ childrenHeight = nodeSize[1]; >+ offsetX = (colWidth - childrenWidth) / 2.0; // half of the space between >+ // columns >+ offsetY = (rowHeight - childrenHeight) / 2.0; // half of the space >+ // between rows >+ } >+ >+ /** >+ * Use this algorithm to layout the given entities and bounds. The entities >+ * will be placed in the same order as they are passed in, unless a >+ * comparator is supplied. >+ * >+ * @param entitiesToLayout >+ * apply the algorithm to these entities >+ * @param bounds >+ * the bounds in which the layout can place the entities. >+ */ >+ protected synchronized void applyLayoutInternal(EntityLayout[] entitiesToLayout, DisplayIndependentRectangle bounds) { >+ >+ int index = 0; >+ for( int i = 0; i < rows; i++ ) { >+ for( int j = 0; j < cols; j++ ) { >+ if( (i*cols + j) < numChildren ) { >+ EntityLayout node = entitiesToLayout[index++]; >+ if (resize && node.isResizable()) >+ node.setSize(Math.max(childrenWidth, MIN_ENTITY_SIZE), Math.max(childrenHeight, MIN_ENTITY_SIZE)); >+ DisplayIndependentDimension size = node.getSize(); >+ double xmove = bounds.x + j * colWidth + offsetX + size.width / 2; >+ double ymove = bounds.y + i * rowHeight + offsetY + size.height / 2; >+ if (node.isMovable()) >+ node.setLocation(xmove, ymove); >+ } >+ } >+ } >+ } >+ >+ /** >+ * Calculates and returns an array containing the number of columns, followed by the number of rows >+ */ >+ protected int[] calculateNumberOfRowsAndCols(int numChildren, double boundX, double boundY, double boundWidth, double boundHeight) { >+ if (aspectRatio == 1.0) { >+ return calculateNumberOfRowsAndCols_square (numChildren, boundX, boundY, boundWidth, boundHeight); >+ } else { >+ return calculateNumberOfRowsAndCols_rectangular (numChildren); >+ } >+ } >+ >+ protected int[] calculateNumberOfRowsAndCols_square (int numChildren, double boundX, double boundY, double boundWidth, double boundHeight) { >+ int rows = Math.max (1, (int) Math.sqrt(numChildren * boundHeight/boundWidth)); >+ int cols = Math.max (1, (int) Math.sqrt(numChildren * boundWidth/boundHeight)); >+ >+ // if space is taller than wide, adjust rows first >+ if (boundWidth <= boundHeight) { >+ //decrease number of rows and columns until just enough or not enough >+ while (rows*cols > numChildren) { >+ if (rows > 1) >+ rows--; >+ if (rows*cols > numChildren) >+ if (cols > 1) >+ cols--; >+ } >+ //increase number of rows and columns until just enough >+ while (rows*cols < numChildren) { >+ rows++; >+ if (rows*cols < numChildren) >+ cols++; >+ } >+ } else { >+ //decrease number of rows and columns until just enough or not enough >+ while (rows*cols > numChildren) { >+ if (cols > 1) >+ cols--; >+ if (rows*cols > numChildren) >+ if (rows > 1) >+ rows--; >+ } >+ //increase number of rows and columns until just enough >+ while (rows*cols < numChildren) { >+ cols++; >+ if (rows*cols < numChildren) >+ rows++; >+ } >+ } >+ int[] result = {cols, rows}; >+ return result; >+ } >+ >+ protected int [] calculateNumberOfRowsAndCols_rectangular (int numChildren) { >+ int rows = Math.max (1, (int)Math.ceil(Math.sqrt(numChildren))); >+ int cols = Math.max (1, (int)Math.ceil(Math.sqrt(numChildren))); >+ int[] result = {cols, rows}; >+ return result; >+ } >+ >+ >+ protected double [] calculateNodeSize (double colWidth, double rowHeight) { >+ double childW = Math.max (MIN_ENTITY_SIZE, PADDING_PERCENTAGE*colWidth); >+ double childH = Math.max (MIN_ENTITY_SIZE, PADDING_PERCENTAGE*(rowHeight - rowPadding)); >+ double whRatio = colWidth/rowHeight; >+ if (whRatio < aspectRatio) { >+ childH = childW / aspectRatio; >+ } else { >+ childW = childH * aspectRatio; >+ } >+ double [] result = {childW, childH}; >+ return result; >+ } >+ >+ /** >+ * Sets the padding between rows in the grid >+ * >+ * @param rowPadding >+ * padding - should be greater than or equal to 0 >+ */ >+ public void setRowPadding(int rowPadding) { >+ if (rowPadding >= 0) { >+ this.rowPadding = rowPadding; >+ } >+ } >+ >+ /** >+ * Sets the preferred aspect ratio for layout entities. The default aspect >+ * ratio is 1. >+ * >+ * @param aspectRatio >+ * aspect ratio - should be greater than 0 >+ */ >+ public void setAspectRatio(double aspectRatio) { >+ if (aspectRatio > 0) { >+ this.aspectRatio = aspectRatio; >+ } >+ } >+ >+ /** >+ * >+ * @return true if this algorithm is set to resize elements >+ */ >+ public boolean isResizing() { >+ return resize; >+ } >+ >+ /** >+ * >+ * @param resizing >+ * true if this algorithm should resize elements (default is >+ * false) >+ */ >+ public void setResizing(boolean resizing) { >+ resize = resizing; >+ } >+ >+} >Index: src/org/eclipse/zest/layouts/algorithms/HorizontalLayoutAlgorithm.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/algorithms/HorizontalLayoutAlgorithm.java >diff -N src/org/eclipse/zest/layouts/algorithms/HorizontalLayoutAlgorithm.java >--- src/org/eclipse/zest/layouts/algorithms/HorizontalLayoutAlgorithm.java 12 Sep 2007 20:44:37 -0000 1.6 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,55 +0,0 @@ >-/******************************************************************************* >- * Copyright 2005, 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: >- * Ian Bull - updated and modified >- *******************************************************************************/ >-package org.eclipse.zest.layouts.algorithms; >- >-import org.eclipse.zest.layouts.LayoutStyles; >- >-/** >- * @version 2.0 >- * @author Casey Best and Rob Lintern >- */ >-public class HorizontalLayoutAlgorithm extends GridLayoutAlgorithm { >- >- >- public HorizontalLayoutAlgorithm(int styles) { >- super(styles); >- } >- >- /** >- * Horizontal Layout Algorithm constructor. Sets the Style to none. >- */ >- public HorizontalLayoutAlgorithm() { >- this( LayoutStyles.NONE ); >- } >- /** >- * Calculates and returns an array containing the number of columns, followed by the number of rows >- */ >- protected int[] calculateNumberOfRowsAndCols (int numChildren, double boundX, double boundY, double boundWidth, double boundHeight) { >- int rows = 1; >- int cols = numChildren; >- int[] result = {cols, rows}; >- return result; >- } >- >- protected boolean isValidConfiguration(boolean asynchronous, boolean continueous) { >- if ( asynchronous && continueous ) { >- return false; >- } else if ( asynchronous && !continueous ) { >- return true; >- } else if ( !asynchronous && continueous ) { >- return false; >- } else if ( !asynchronous && !continueous ) { >- return true; >- } >- >- return false; >- } >-} >Index: src/org/eclipse/zest/layouts/algorithms/HorizontalShift.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/algorithms/HorizontalShift.java >diff -N src/org/eclipse/zest/layouts/algorithms/HorizontalShift.java >--- src/org/eclipse/zest/layouts/algorithms/HorizontalShift.java 12 Sep 2007 20:44:37 -0000 1.6 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,131 +0,0 @@ >-/******************************************************************************* >- * Copyright 2006, 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.layouts.algorithms; >- >-import java.util.ArrayList; >-import java.util.Collections; >-import java.util.Comparator; >-import java.util.Iterator; >-import java.util.List; >- >-import org.eclipse.zest.layouts.LayoutEntity; >-import org.eclipse.zest.layouts.dataStructures.InternalNode; >-import org.eclipse.zest.layouts.dataStructures.InternalRelationship; >- >-/** >- * This layout shifts overlapping nodes to the right. >- * @author Ian Bull >- */ >-public class HorizontalShift extends AbstractLayoutAlgorithm { >- >- private static final double DELTA = 10; >- private static final double VSPACING = 2; >- >- public HorizontalShift(int styles) { >- super(styles); >- } >- >- protected void applyLayoutInternal(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, >- double boundsX, double boundsY, double boundsWidth, double boundsHeight) { >- >- ArrayList row = new ArrayList(); >- for ( int i =0; i < entitiesToLayout.length; i++) { >- addToRowList(entitiesToLayout[i], row); >- } >- >- int heightSoFar = 0; >- >- Collections.sort(row, new Comparator() { >- >- public int compare(Object arg0, Object arg1) { >- // TODO Auto-generated method stub >- List a0 = (List) arg0; >- List a1 = (List) arg1; >- LayoutEntity node0 = ((InternalNode)a0.get(0)).getLayoutEntity(); >- LayoutEntity node1 = ((InternalNode)a1.get(0)).getLayoutEntity(); >- return (int) (node0.getYInLayout() - (node1.getYInLayout())); >- } >- >- }); >- >- Iterator iterator = row.iterator(); >- while (iterator.hasNext() ) { >- List currentRow = (List) iterator.next(); >- Collections.sort(currentRow, new Comparator() { >- public int compare(Object arg0, Object arg1) { >- return (int) (((InternalNode)arg1).getLayoutEntity().getYInLayout() - ((InternalNode)arg0).getLayoutEntity().getYInLayout()); >- } >- }); >- Iterator iterator2 = currentRow.iterator(); >- int i = 0; >- int width = (int) ((boundsWidth / 2) - currentRow.size() * 75); >- >- heightSoFar += ((InternalNode)currentRow.get(0)).getLayoutEntity().getHeightInLayout() + VSPACING*8 ; >- while(iterator2.hasNext()) { >- InternalNode currentNode = (InternalNode) iterator2.next(); >- >- double location = width + 10*++i; >- currentNode.setLocation(location , heightSoFar); >- width += currentNode.getLayoutEntity().getWidthInLayout(); >- } >- } >- } >- >- >- private void addToRowList( InternalNode node, ArrayList list) { >- double layoutY = node.getLayoutEntity().getYInLayout(); >- >- for ( int i = 0; i < list.size(); i++ ) { >- List currentRow = (List) list.get(i); >- InternalNode currentRowNode = (InternalNode) currentRow.get(0); >- double currentRowY = currentRowNode.getLayoutEntity().getYInLayout(); >- //double currentRowHeight = currentRowNode.getLayoutEntity().getHeightInLayout(); >- if ( layoutY >= (currentRowY-DELTA) && layoutY <= currentRowY + DELTA ) { >- currentRow.add(node); >- //list.add(i, currentRow); >- return; >- } >- } >- List newRow = new ArrayList(); >- newRow.add(node); >- list.add(newRow); >- } >- >- protected int getCurrentLayoutStep() { >- // TODO Auto-generated method stub >- return 0; >- } >- >- protected int getTotalNumberOfLayoutSteps() { >- // TODO Auto-generated method stub >- return 0; >- } >- >- protected boolean isValidConfiguration(boolean asynchronous, boolean continuous) { >- // TODO Auto-generated method stub >- return true; >- } >- >- protected void postLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider) { >- // TODO Auto-generated method stub >- } >- >- protected void preLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, >- double x, double y, double width, double height) { >- // TODO Auto-generated method stub >- >- } >- >- public void setLayoutArea(double x, double y, double width, double height) { >- // TODO Auto-generated method stub >- } >-} >- >Index: src/org/eclipse/zest/layouts/algorithms/HorizontalShiftAlgorithm.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/algorithms/HorizontalShiftAlgorithm.java >diff -N src/org/eclipse/zest/layouts/algorithms/HorizontalShiftAlgorithm.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/zest/layouts/algorithms/HorizontalShiftAlgorithm.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,103 @@ >+/******************************************************************************* >+ * Copyright (c) 2005-2010 The Chisel Group and others. 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 - initial API and implementation >+ * Mateusz Matela >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.layouts.algorithms; >+ >+import java.util.ArrayList; >+import java.util.Collections; >+import java.util.Comparator; >+import java.util.Iterator; >+import java.util.List; >+ >+import org.eclipse.zest.layouts.LayoutAlgorithm; >+import org.eclipse.zest.layouts.dataStructures.DisplayIndependentDimension; >+import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle; >+import org.eclipse.zest.layouts.interfaces.EntityLayout; >+import org.eclipse.zest.layouts.interfaces.LayoutContext; >+ >+/** >+ * This layout shifts overlapping nodes to the right. >+ * >+ * @author Ian Bull >+ */ >+public class HorizontalShiftAlgorithm implements LayoutAlgorithm { >+ >+ private static final double DELTA = 10; >+ >+ private static final double VSPACING = 16; >+ >+ private LayoutContext context; >+ >+ public void applyLayout(boolean clean) { >+ if (!clean) >+ return; >+ ArrayList rowsList = new ArrayList(); >+ EntityLayout[] entities = context.getEntities(); >+ >+ for (int i = 0; i < entities.length; i++) { >+ addToRowList(entities[i], rowsList); >+ } >+ >+ Collections.sort(rowsList, new Comparator() { >+ public int compare(Object o1, Object o2) { >+ List a0 = (List) o1; >+ List a1 = (List) o2; >+ EntityLayout entity0 = (EntityLayout) a0.get(0); >+ EntityLayout entity1 = (EntityLayout) a1.get(0); >+ return (int) (entity0.getLocation().y - entity1.getLocation().y); >+ } >+ }); >+ >+ Comparator entityComparator = new Comparator() { >+ public int compare(Object o1, Object o2) { >+ return (int) (((EntityLayout) o1).getLocation().y - ((EntityLayout) o2).getLocation().y); >+ } >+ }; >+ DisplayIndependentRectangle bounds = context.getBounds(); >+ int heightSoFar = 0; >+ >+ for (Iterator iterator = rowsList.iterator(); iterator.hasNext();) { >+ List currentRow = (List) iterator.next(); >+ Collections.sort(currentRow, entityComparator); >+ >+ int i = 0; >+ int width = (int) (bounds.width / 2 - currentRow.size() * 75); >+ >+ heightSoFar += ((EntityLayout) currentRow.get(0)).getSize().height + VSPACING; >+ for (Iterator iterator2 = currentRow.iterator(); iterator2.hasNext();) { >+ EntityLayout entity = (EntityLayout) iterator2.next(); >+ DisplayIndependentDimension size = entity.getSize(); >+ entity.setLocation(width + 10 * ++i + size.width / 2, heightSoFar + size.height / 2); >+ width += size.width; >+ } >+ } >+ } >+ >+ public void setLayoutContext(LayoutContext context) { >+ this.context = context; >+ } >+ >+ private void addToRowList(EntityLayout entity, ArrayList rowsList) { >+ double layoutY = entity.getLocation().y; >+ >+ for (Iterator iterator = rowsList.iterator(); iterator.hasNext();) { >+ List currentRow = (List) iterator.next(); >+ EntityLayout currentRowEntity = (EntityLayout) currentRow.get(0); >+ double currentRowY = currentRowEntity.getLocation().y; >+ if (layoutY >= currentRowY - DELTA && layoutY <= currentRowY + DELTA) { >+ currentRow.add(entity); >+ return; >+ } >+ } >+ List newRow = new ArrayList(); >+ newRow.add(entity); >+ rowsList.add(newRow); >+ } >+} >Index: src/org/eclipse/zest/layouts/algorithms/HorizontalTreeLayoutAlgorithm.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/algorithms/HorizontalTreeLayoutAlgorithm.java >diff -N src/org/eclipse/zest/layouts/algorithms/HorizontalTreeLayoutAlgorithm.java >--- src/org/eclipse/zest/layouts/algorithms/HorizontalTreeLayoutAlgorithm.java 12 Sep 2007 20:44:37 -0000 1.5 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,68 +0,0 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts.algorithms; >- >-import org.eclipse.zest.layouts.LayoutStyles; >-import org.eclipse.zest.layouts.dataStructures.InternalNode; >-import org.eclipse.zest.layouts.dataStructures.InternalRelationship; >- >- >- >- >-/** >- * A simple algorithm to arrange graph nodes in a layered horizontal tree-like layout. >- * @see TreeLayoutAlgorithm >- * >- * @version 1.0 >- * @author Rob Lintern >- */ >-public class HorizontalTreeLayoutAlgorithm extends TreeLayoutAlgorithm { >- >- >- /** >- * Creates a horizontal tree layout with no style >- */ >- public HorizontalTreeLayoutAlgorithm() { >- this( LayoutStyles.NONE ); >- } >- >- /** >- * >- */ >- public HorizontalTreeLayoutAlgorithm( int styles ) { >- super( styles ); >- } >- >- protected void preLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height) { >- // NOTE: width and height are swtiched here when calling super method >- super.preLayoutAlgorithm(entitiesToLayout, relationshipsToConsider, x, y, height, width); >- } >- >- protected void postLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider) { >- // swap x->y and width->height >- for (int i = 0; i < entitiesToLayout.length; i++) { >- InternalNode entity = entitiesToLayout[i]; >- entity.setInternalLocation(entity.getInternalY(), entity.getInternalX() ); >- entity.setInternalSize( entity.getInternalWidth(), entity.getInternalHeight() ); >- } >- super.postLayoutAlgorithm(entitiesToLayout, relationshipsToConsider); >- } >- >- protected boolean isValidConfiguration(boolean asynchronous, boolean continueous) { >- if ( asynchronous && continueous ) return false; >- else if ( asynchronous && !continueous ) return true; >- else if ( !asynchronous && continueous ) return false; >- else if ( !asynchronous && !continueous ) return true; >- >- return false; >- } >- >-} >Index: src/org/eclipse/zest/layouts/algorithms/RadialLayoutAlgorithm.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.layouts/src/org/eclipse/zest/layouts/algorithms/RadialLayoutAlgorithm.java,v >retrieving revision 1.5 >diff -u -r1.5 RadialLayoutAlgorithm.java >--- src/org/eclipse/zest/layouts/algorithms/RadialLayoutAlgorithm.java 12 Sep 2007 20:44:37 -0000 1.5 >+++ src/org/eclipse/zest/layouts/algorithms/RadialLayoutAlgorithm.java 15 Mar 2010 03:49:07 -0000 >@@ -1,149 +1,101 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts.algorithms; >- >-import java.util.Iterator; >-import java.util.List; >- >-import org.eclipse.zest.layouts.LayoutStyles; >-import org.eclipse.zest.layouts.dataStructures.DisplayIndependentPoint; >-import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle; >-import org.eclipse.zest.layouts.dataStructures.InternalNode; >-import org.eclipse.zest.layouts.dataStructures.InternalRelationship; >- >- >-/** >- * This layout will take the given entities, apply a tree layout to them, and then display the >- * tree in a circular fashion with the roots in the center. >- * >- * @author Casey Best >- * @auhtor Rob Lintern >- */ >-public class RadialLayoutAlgorithm extends TreeLayoutAlgorithm { >- private static final double MAX_DEGREES = Math.PI * 2; >- private double startDegree; >- private double endDegree; >- private TreeLayoutAlgorithm treeLayout; >- private List roots; >- >- >- /** >- * Creates a radial layout with no style. >- */ >- public RadialLayoutAlgorithm() { >- this ( LayoutStyles.NONE ); >- } >- >- //TODO: This is a really strange pattern. It extends tree layout and it contains a tree layout ? >- public RadialLayoutAlgorithm ( int styles ) { >- super( styles ); >- treeLayout = new TreeLayoutAlgorithm( styles ); >- startDegree = 0; >- endDegree = MAX_DEGREES; >- } >- >- public void setLayoutArea(double x, double y, double width, double height) { >- throw new RuntimeException("Operation not implemented"); >- } >- >- >- protected boolean isValidConfiguration(boolean asynchronous, boolean continueous) { >- if ( asynchronous && continueous ) return false; >- else if ( asynchronous && !continueous ) return true; >- else if ( !asynchronous && continueous ) return false; >- else if ( !asynchronous && !continueous ) return true; >- >- return false; >- } >- >- >- DisplayIndependentRectangle layoutBounds = null; >- protected void preLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height) { >- // TODO Auto-generated method stub >- layoutBounds = new DisplayIndependentRectangle(x, y, width, height); >- super.preLayoutAlgorithm(entitiesToLayout, relationshipsToConsider, x, y, >- width, height); >- } >- >- protected void postLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider) { >- roots = treeLayout.getRoots(); >- computeRadialPositions (entitiesToLayout, layoutBounds); >- >- defaultFitWithinBounds(entitiesToLayout, layoutBounds); >- >- super.postLayoutAlgorithm(entitiesToLayout, relationshipsToConsider); >- >- } >- >- /** >- * Set the range the radial layout will use when applyLayout is called. >- * Both values must be in radians. >- */ >- public void setRangeToLayout (double startDegree, double endDegree) { >- this.startDegree = startDegree; >- this.endDegree = endDegree; >- } >- >- /** >- * Take the tree and make it round. This is done by determining the location of each entity in terms >- * of its percentage in the tree layout. Then apply that percentage to the radius and distance from >- * the center. >- */ >- protected void computeRadialPositions (InternalNode[] entities, DisplayIndependentRectangle bounds2) { //TODO TODO TODO >- DisplayIndependentRectangle bounds = new DisplayIndependentRectangle(getLayoutBounds(entities, true)); >- bounds.height = bounds2.height; >- bounds.y = bounds2.y; >- for (int i = 0; i < entities.length; i++) { >- InternalNode entity = entities[i]; >- double percentTheta = (entity.getInternalX() - bounds.x) / bounds.width; >- double distance = (entity.getInternalY() - bounds.y) / bounds.height; >- double theta = startDegree + Math.abs(endDegree - startDegree) * percentTheta; >- double newX = distance * Math.cos (theta); >- double newY = distance * Math.sin (theta); >- >- entity.setInternalLocation( newX, newY ); >- } >- } >- >- /** >- * Find the bounds in which the nodes are located. Using the bounds against the real bounds >- * of the screen, the nodes can proportionally be placed within the real bounds. >- * The bounds can be determined either including the size of the nodes or not. If the size >- * is not included, the bounds will only be guaranteed to include the center of each node. >- */ >- protected DisplayIndependentRectangle getLayoutBounds (InternalNode[] entitiesToLayout, boolean includeNodeSize) { >- DisplayIndependentRectangle layoutBounds = super.getLayoutBounds(entitiesToLayout, includeNodeSize); >- DisplayIndependentPoint centerPoint = (roots != null) ? determineCenterPoint(roots) : >- new DisplayIndependentPoint (layoutBounds.x + layoutBounds.width / 2, layoutBounds.y + layoutBounds.height / 2); >- // The center entity is determined in applyLayout >- double maxDistanceX = Math.max( >- Math.abs (layoutBounds.x + layoutBounds.width - centerPoint.x), >- Math.abs (centerPoint.x - layoutBounds.x)); >- double maxDistanceY = Math.max( >- Math.abs (layoutBounds.y + layoutBounds.height - centerPoint.y), >- Math.abs (centerPoint.y - layoutBounds.y)); >- layoutBounds = new DisplayIndependentRectangle (centerPoint.x - maxDistanceX, centerPoint.y - maxDistanceY, maxDistanceX * 2, maxDistanceY * 2); >- return layoutBounds; >- } >- >- /** >- * Find the center point between the roots >- */ >- private DisplayIndependentPoint determineCenterPoint (List roots) { >- double totalX = 0, totalY = 0; >- for ( Iterator iterator = roots.iterator(); iterator.hasNext(); ) { >- InternalNode entity = (InternalNode)iterator.next(); >- totalX += entity.getInternalX(); >- totalY += entity.getInternalY(); >- } >- return new DisplayIndependentPoint (totalX / roots.size(), totalY / roots.size()); >- } >-} >+/******************************************************************************* >+ * Copyright (c) 2005-2010 The Chisel Group and others. 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 - initial API and implementation >+ * Mateusz Matela >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.layouts.algorithms; >+ >+import org.eclipse.zest.layouts.LayoutAlgorithm; >+import org.eclipse.zest.layouts.dataStructures.DisplayIndependentPoint; >+import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle; >+import org.eclipse.zest.layouts.interfaces.EntityLayout; >+import org.eclipse.zest.layouts.interfaces.LayoutContext; >+ >+/** >+ * This layout will take the given entities, apply a tree layout to them, and >+ * then display the tree in a circular fashion with the roots in the center. >+ * >+ * @author Casey Best >+ * @auhtor Rob Lintern >+ */ >+public class RadialLayoutAlgorithm implements LayoutAlgorithm { >+ >+ private static final double MAX_DEGREES = Math.PI * 2; >+ private double startDegree = 0; >+ private double endDegree = MAX_DEGREES; >+ >+ private LayoutContext context; >+ private boolean resize = false; >+ >+ private TreeLayoutAlgorithm treeLayout = new TreeLayoutAlgorithm(); >+ >+ public void applyLayout(boolean clean) { >+ if (!clean) >+ return; >+ treeLayout.internalApplyLayout(); >+ EntityLayout[] entities = context.getEntities(); >+ DisplayIndependentRectangle bounds = context.getBounds(); >+ computeRadialPositions(entities, bounds); >+ if (resize) >+ AlgorithmHelper.maximizeSizes(entities); >+ int insets = 4; >+ bounds.x += insets; >+ bounds.y += insets; >+ bounds.width -= 2 * insets; >+ bounds.height -= 2 * insets; >+ AlgorithmHelper.fitWithinBounds(entities, bounds, resize); >+ } >+ >+ private void computeRadialPositions(EntityLayout[] entities, DisplayIndependentRectangle bounds) { >+ DisplayIndependentRectangle layoutBounds = AlgorithmHelper.getLayoutBounds(entities, false); >+ layoutBounds.x = bounds.x; >+ layoutBounds.width = bounds.width; >+ for (int i = 0; i < entities.length; i++) { >+ DisplayIndependentPoint location = entities[i].getLocation(); >+ double percenttheta = (location.x - layoutBounds.x) / layoutBounds.width; >+ double distance = (location.y - layoutBounds.y) / layoutBounds.height; >+ double theta = startDegree + Math.abs(endDegree - startDegree) * percenttheta; >+ location.x = distance * Math.cos(theta); >+ location.y = distance * Math.sin(theta); >+ entities[i].setLocation(location.x, location.y); >+ } >+ } >+ >+ public void setLayoutContext(LayoutContext context) { >+ this.context = context; >+ treeLayout.setLayoutContext(context); >+ } >+ >+ /** >+ * Set the range the radial layout will use when applyLayout is called. Both >+ * values must be in radians. >+ */ >+ public void setRangeToLayout(double startDegree, double endDegree) { >+ this.startDegree = startDegree; >+ this.endDegree = endDegree; >+ } >+ >+ /** >+ * >+ * @return true if this algorithm is set to resize elements >+ */ >+ public boolean isResizing() { >+ return resize; >+ } >+ >+ /** >+ * >+ * @param resizing >+ * true if this algorithm should resize elements (default is >+ * false) >+ */ >+ public void setResizing(boolean resizing) { >+ resize = resizing; >+ treeLayout.setResizing(resize); >+ } >+} >Index: src/org/eclipse/zest/layouts/algorithms/SpaceTreeLayoutAlgorithm.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/algorithms/SpaceTreeLayoutAlgorithm.java >diff -N src/org/eclipse/zest/layouts/algorithms/SpaceTreeLayoutAlgorithm.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/zest/layouts/algorithms/SpaceTreeLayoutAlgorithm.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,1204 @@ >+/******************************************************************************* >+ * Copyright (c) 2005-2010 The Chisel Group and others. 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 - initial API and implementation >+ * Mateusz Matela >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.layouts.algorithms; >+ >+import java.util.ArrayList; >+import java.util.Arrays; >+import java.util.Collections; >+import java.util.Comparator; >+import java.util.Iterator; >+import java.util.LinkedList; >+import java.util.List; >+import java.util.ListIterator; >+ >+import org.eclipse.zest.layouts.LayoutAlgorithm; >+import org.eclipse.zest.layouts.dataStructures.DisplayIndependentDimension; >+import org.eclipse.zest.layouts.dataStructures.DisplayIndependentPoint; >+import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle; >+import org.eclipse.zest.layouts.interfaces.ContextListener; >+import org.eclipse.zest.layouts.interfaces.ExpandCollapseManager; >+import org.eclipse.zest.layouts.interfaces.LayoutContext; >+import org.eclipse.zest.layouts.interfaces.LayoutListener; >+import org.eclipse.zest.layouts.interfaces.NodeLayout; >+import org.eclipse.zest.layouts.interfaces.SubgraphLayout; >+ >+/** >+ * Layout algorithm implementing SpaceTree. It assumes that nodes in the layout >+ * context make a tree structure. >+ * >+ * It expands and collapses nodes to optimize use of available space. In order >+ * to keep the tree structure clearly visible, it also keeps track of the nodes' >+ * positions to makes sure they stay in their current layer and don't overlap >+ * with each other. >+ * >+ * It's recommended to set an <code>ExpandCollapseManger</code> returned by >+ * {@link #getExpandCollapseManager()} in the <code>LayoutContext</code> that >+ * will be used with this layout algorithm. Other >+ * <code>ExpandCollapseManager</code>s could also work, but the algorithm's >+ * functionality would be very limited. >+ */ >+public class SpaceTreeLayoutAlgorithm implements LayoutAlgorithm { >+ >+ /** >+ * Tree direction constant for which root is placed at the top and branches >+ * spread downwards >+ */ >+ public final static int TOP_DOWN = 1; >+ >+ /** >+ * Tree direction constant for which root is placed at the bottom and >+ * branches spread upwards >+ */ >+ public final static int BOTTOM_UP = 2; >+ >+ /** >+ * Tree direction constant for which root is placed at the left and branches >+ * spread to the right >+ */ >+ public final static int LEFT_RIGHT = 3; >+ >+ /** >+ * Tree direction constant for which root is placed at the right and >+ * branches spread to the left >+ */ >+ public final static int RIGHT_LEFT = 4; >+ >+ private class SpaceTreeNode extends TreeLayoutObserver.TreeNode { >+ public SubgraphLayout subgraph = null; >+ >+ public boolean expanded = false; >+ public double positionInLayer; >+ >+ public SpaceTreeNode(NodeLayout node, TreeLayoutObserver owner) { >+ super(node, owner); >+ } >+ >+ protected void addChild(TreeLayoutObserver.TreeNode child) { >+ super.addChild(child); >+ >+ SpaceTreeNode child2 = (SpaceTreeNode) child; >+ child2.expanded = false; >+ child2.setSubgraph(null); >+ >+ if (child.depth >= 0) >+ ((SpaceTreeLayer) spaceTreeLayers.get(child.depth)).removeNode(child2); >+ >+ if (expanded) { >+ child.depth = this.depth + 1; >+ >+ SpaceTreeLayer childLayer; >+ if (child.depth < spaceTreeLayers.size()) >+ childLayer = ((SpaceTreeLayer) spaceTreeLayers.get(child.depth)); >+ else >+ spaceTreeLayers.add(childLayer = new SpaceTreeLayer(child.depth)); >+ >+ if (childLayer.nodes.isEmpty()) >+ child.order = 0; >+ else >+ child.order = ((SpaceTreeNode) childLayer.nodes.get(childLayer.nodes.size() - 1)).order + 1; >+ childLayer.addNodes(Arrays.asList(new Object[] { child })); >+ } >+ } >+ >+ public void precomputeTree() { >+ super.precomputeTree(); >+ if (this == owner.getSuperRoot()) { >+ expanded = true; >+ while (spaceTreeLayers.size() <= this.height) >+ spaceTreeLayers.add(new SpaceTreeLayer(spaceTreeLayers.size())); >+ >+ if (treeObserver != null) >+ refreshLayout(true); >+ } >+ } >+ >+ public SubgraphLayout collapseAllChildrenIntoSubgraph(SubgraphLayout subgraph, boolean includeYourself) { >+ expanded = false; >+ ArrayList allChildren = new ArrayList(); >+ LinkedList nodesToVisit = new LinkedList(); >+ nodesToVisit.addLast(this); >+ while (!nodesToVisit.isEmpty()) { >+ SpaceTreeNode currentNode = (SpaceTreeNode) nodesToVisit.removeFirst(); >+ for (Iterator iterator = currentNode.children.iterator(); iterator.hasNext();) { >+ SpaceTreeNode child = (SpaceTreeNode) iterator.next(); >+ allChildren.add(child.node); >+ child.setSubgraph(null); >+ child.expanded = false; >+ nodesToVisit.addLast(child); >+ } >+ } >+ if (includeYourself) >+ allChildren.add(this.node); >+ if (allChildren.isEmpty()) { >+ setSubgraph(null); >+ return null; >+ } >+ NodeLayout[] childrenArray = (NodeLayout[]) allChildren.toArray(new NodeLayout[allChildren.size()]); >+ if (subgraph == null) { >+ subgraph = context.createSubgraph(childrenArray); >+ subgraph.setDirection(getSubgraphDirection()); >+ } else { >+ subgraph.addNodes(childrenArray); >+ } >+ if (!includeYourself) >+ setSubgraph(subgraph); >+ return subgraph; >+ } >+ >+ public void setSubgraph(SubgraphLayout subgraph) { // ! >+ if (this.subgraph != subgraph) { >+ this.subgraph = subgraph; >+ refreshSubgraphLocation(); >+ } >+ } >+ >+ /** >+ * Moves the node back to its layer, as close as possible to given >+ * preferred location >+ * >+ * @param preferredLocation >+ */ >+ public void adjustPosition(DisplayIndependentPoint preferredLocation) { // ! >+ protectedNode = (SpaceTreeNode) owner.getSuperRoot(); >+ >+ double newPositionInLayer = (direction == BOTTOM_UP || direction == TOP_DOWN) ? preferredLocation.x : preferredLocation.y; >+ if (((SpaceTreeNode) parent).expanded) { >+ ((SpaceTreeLayer) spaceTreeLayers.get(depth)).moveNode(this, newPositionInLayer); >+ centerParentsTopDown(); >+ } >+ } >+ >+ public void refreshSubgraphLocation() { >+ if (subgraph != null && subgraph.isGraphEntity()) { >+ DisplayIndependentPoint nodeLocation = node.getLocation(); >+ DisplayIndependentDimension nodeSize = node.getSize(); >+ DisplayIndependentDimension subgraphSize = subgraph.getSize(); >+ double x = 0, y = 0; >+ switch (direction) { >+ case TOP_DOWN: >+ x = nodeLocation.x; >+ y = nodeLocation.y + (nodeSize.height + subgraphSize.height) / 2; >+ break; >+ case BOTTOM_UP: >+ x = nodeLocation.x; >+ y = nodeLocation.y - (nodeSize.height + subgraphSize.height) / 2; >+ break; >+ case LEFT_RIGHT: >+ x = nodeLocation.x + (nodeSize.width + subgraphSize.width) / 2; >+ y = nodeLocation.y; >+ break; >+ case RIGHT_LEFT: >+ x = nodeLocation.x - (nodeSize.width + subgraphSize.height) / 2; >+ y = nodeLocation.y; >+ break; >+ } >+ subgraph.setLocation(x, y); >+ } >+ ((SpaceTreeLayer) spaceTreeLayers.get(depth)).refreshThickness(); >+ } >+ >+ public double spaceRequiredForNode() { >+ if (node == null) >+ return 0; >+ switch (direction) { >+ case TOP_DOWN: >+ case BOTTOM_UP: >+ return node.getSize().width; >+ case LEFT_RIGHT: >+ case RIGHT_LEFT: >+ return node.getSize().height; >+ } >+ throw new RuntimeException("invalid direction"); >+ } >+ >+ public double spaceRequiredForChildren() { >+ if (children.isEmpty()) >+ return 0; >+ double result = 0; >+ for (Iterator iterator = children.iterator(); iterator.hasNext();) { >+ SpaceTreeNode child = (SpaceTreeNode) iterator.next(); >+ result += child.spaceRequiredForNode(); >+ } >+ result += leafGap * (children.size() - 1); >+ return result; >+ } >+ >+ /** >+ * Checks if nodes in given list have proper positions according to >+ * their children (a parent's position cannot be smaller than its first >+ * child's position nor bigger than its last child's position). If not, >+ * it tries to fix them. >+ * >+ * @param nodesToCheck >+ * @return true if all locations are correct or could be corrected while >+ * checking. >+ */ >+ public boolean childrenPositionsOK(ArrayList nodesToCheck) { >+ for (Iterator iterator = nodesToCheck.iterator(); iterator.hasNext();) { >+ SpaceTreeNode node = (SpaceTreeNode) iterator.next(); >+ if (node.depth < 0 || node.children.isEmpty()) >+ continue; >+ SpaceTreeNode child = ((SpaceTreeNode) node.children.get(0)); >+ if (child.positionInLayer > node.positionInLayer) { >+ ((SpaceTreeLayer) spaceTreeLayers.get(node.depth)).moveNode(node, child.positionInLayer); >+ if (child.positionInLayer > node.positionInLayer) { >+ ((SpaceTreeLayer) spaceTreeLayers.get(child.depth)).moveNode(child, node.positionInLayer); >+ if (child.positionInLayer > node.positionInLayer) { >+ return false; >+ } >+ } >+ } >+ child = ((SpaceTreeNode) node.children.get(node.children.size() - 1)); >+ if (child.positionInLayer < node.positionInLayer) { >+ ((SpaceTreeLayer) spaceTreeLayers.get(node.depth)).moveNode(node, child.positionInLayer); >+ if (child.positionInLayer < node.positionInLayer) { >+ ((SpaceTreeLayer) spaceTreeLayers.get(child.depth)).moveNode(child, node.positionInLayer); >+ if (child.positionInLayer < node.positionInLayer) { >+ return false; >+ } >+ } >+ } >+ } >+ return true; >+ } >+ >+ public void centerParentsBottomUp() { >+ if (!children.isEmpty() && expanded) { >+ for (Iterator iterator = children.iterator(); iterator.hasNext();) { >+ ((SpaceTreeNode) iterator.next()).centerParentsBottomUp(); >+ } >+ >+ if (depth >= 0) { >+ SpaceTreeNode firstChild = (SpaceTreeNode) children.get(0); >+ SpaceTreeNode lastChild = (SpaceTreeNode) children.get(children.size() - 1); >+ SpaceTreeLayer layer = (SpaceTreeLayer) spaceTreeLayers.get(depth); >+ layer.moveNode(this, (firstChild.positionInLayer + lastChild.positionInLayer) / 2); >+ } >+ } >+ } >+ >+ public void centerParentsTopDown() { >+ if (this == owner.getSuperRoot()) { >+ this.positionInLayer = getAvailableSpace() / 2; >+ } >+ if (!children.isEmpty() && expanded) { >+ SpaceTreeNode firstChild = (SpaceTreeNode) children.get(0); >+ SpaceTreeNode lastChild = (SpaceTreeNode) children.get(children.size() - 1); >+ double offset = this.positionInLayer - (firstChild.positionInLayer + lastChild.positionInLayer) / 2; >+ if (firstChild.positionInLayer - firstChild.spaceRequiredForNode() / 2 + offset < 0) >+ offset = -firstChild.positionInLayer + firstChild.spaceRequiredForNode() / 2; >+ double availableSpace = getAvailableSpace(); >+ if (lastChild.positionInLayer + lastChild.spaceRequiredForNode() / 2 + offset > availableSpace) { >+ offset = availableSpace - lastChild.positionInLayer - lastChild.spaceRequiredForNode() / 2; >+ } >+ SpaceTreeLayer layer = (SpaceTreeLayer) spaceTreeLayers.get(depth + 1); >+ layer.fitNodesWithinBounds(children, firstChild.positionInLayer + offset, lastChild.positionInLayer + offset); >+ >+ for (Iterator iterator = children.iterator(); iterator.hasNext();) { >+ ((SpaceTreeNode) iterator.next()).centerParentsTopDown(); >+ } >+ } >+ } >+ >+ public void flushExpansionChanges() { >+ if (node != null) >+ node.prune(null); >+ if (this.expanded) { >+ setSubgraph(null); >+ for (Iterator iterator = children.iterator(); iterator.hasNext();) { >+ ((SpaceTreeNode) iterator.next()).flushExpansionChanges(); >+ } >+ } >+ if (!this.expanded && subgraph == null) { >+ collapseAllChildrenIntoSubgraph(null, false); >+ } >+ } >+ >+ public boolean flushCollapseChanges() { >+ if (!expanded) { >+ int numberOfChildrenInSubgraph = subgraph == null ? 0 : subgraph.countNodes(); >+ collapseAllChildrenIntoSubgraph(subgraph, false); >+ int newNumberOfChildrenInSubgraph = (subgraph == null ? 0 : subgraph.countNodes()); >+ if (numberOfChildrenInSubgraph != newNumberOfChildrenInSubgraph && newNumberOfChildrenInSubgraph > 0) >+ refreshSubgraphLocation(); >+ return numberOfChildrenInSubgraph != newNumberOfChildrenInSubgraph; >+ } >+ if (expanded && subgraph == null) { >+ boolean madeChagnes = false; >+ for (Iterator iterator = children.iterator(); iterator.hasNext();) { >+ madeChagnes = ((SpaceTreeNode) iterator.next()).flushCollapseChanges() || madeChagnes; >+ } >+ return madeChagnes; >+ } >+ return false; >+ } >+ >+ /** >+ * Sets locations of nodes in the graph depending on their current layer >+ * and position in layer. >+ * >+ * @param thicknessSoFar >+ * sum of thicknesses and gaps for all layers 'above' this >+ * node (should be 0 if called on superRoot) >+ * @return true if location of at least one node has changed >+ */ >+ public boolean flushLocationChanges(double thicknessSoFar) { >+ boolean madeChanges = false; >+ if (node != null) { >+ DisplayIndependentDimension nodeSize = node.getSize(); >+ double x = 0, y = 0; >+ switch (direction) { >+ case TOP_DOWN: >+ x = bounds.x + positionInLayer; >+ y = thicknessSoFar + nodeSize.height / 2; >+ break; >+ case BOTTOM_UP: >+ x = bounds.x + positionInLayer; >+ y = bounds.y + bounds.height - thicknessSoFar - nodeSize.height / 2; >+ break; >+ case LEFT_RIGHT: >+ x = thicknessSoFar + nodeSize.height / 2; >+ y = bounds.y + positionInLayer; >+ break; >+ case RIGHT_LEFT: >+ x = bounds.x + bounds.width - thicknessSoFar - nodeSize.height / 2; >+ y = bounds.y + positionInLayer; >+ break; >+ } >+ DisplayIndependentPoint currentLocation = node.getLocation(); >+ if (currentLocation.x != x || currentLocation.y != y) { >+ node.setLocation(x, y); >+ refreshSubgraphLocation(); >+ madeChanges = true; >+ } >+ } >+ if (expanded && subgraph == null) { >+ thicknessSoFar += (depth >= 0 ? ((SpaceTreeLayer) spaceTreeLayers.get(depth)).thickness : 0) + layerGap; >+ for (Iterator iterator = children.iterator(); iterator.hasNext();) { >+ SpaceTreeNode child = (SpaceTreeNode) iterator.next(); >+ madeChanges = child.flushLocationChanges(thicknessSoFar) || madeChanges; >+ } >+ } >+ return madeChanges; >+ } >+ >+ public String toString() { >+ StringBuffer sb = new StringBuffer(); >+ for (int i = 0; i < depth; i++) >+ sb.append(" "); >+ if (node != null) >+ sb.append(node.toString()); >+ sb.append("|" + this.order); >+ sb.append('\n'); >+ for (Iterator iterator = children.iterator(); iterator.hasNext();) { >+ SpaceTreeNode child = (SpaceTreeNode) iterator.next(); >+ sb.append(child.toString()); >+ } >+ return sb.toString(); >+ } >+ } >+ >+ private TreeLayoutObserver.TreeNodeFactory spaceTreeNodeFactory = new TreeLayoutObserver.TreeNodeFactory() { >+ public TreeLayoutObserver.TreeNode createTreeNode(NodeLayout nodeLayout, TreeLayoutObserver observer) { >+ return new SpaceTreeNode(nodeLayout, observer); >+ }; >+ }; >+ >+ private class SpaceTreeLayer { >+ public ArrayList nodes = new ArrayList(); >+ private final int depth; >+ public double thickness = 0; >+ >+ public SpaceTreeLayer(int depth) { >+ this.depth = depth; >+ } >+ >+ public void addNodes(List nodesToAdd) { >+ ListIterator layerIterator = nodes.listIterator(); >+ SpaceTreeNode previousNode = null; >+ for (Iterator iterator = nodesToAdd.iterator(); iterator.hasNext();) { >+ SpaceTreeNode nodeToAdd = (SpaceTreeNode) iterator.next(); >+ >+ SpaceTreeNode nodeInLayer = null; >+ while (layerIterator.hasNext()) { >+ nodeInLayer = (SpaceTreeNode) layerIterator.next(); >+ if (nodeInLayer.order >= nodeToAdd.order) >+ break; >+ double expectedPostion = (previousNode == null) ? 0 : previousNode.positionInLayer + expectedDistance(previousNode, nodeInLayer); >+ nodeInLayer.positionInLayer = Math.max(nodeInLayer.positionInLayer, expectedPostion); >+ previousNode = nodeInLayer; >+ } >+ >+ if (nodeInLayer == null) { >+ layerIterator.add(nodeToAdd); >+ } else if (nodeInLayer.order == nodeToAdd.order) { >+ layerIterator.set(nodeToAdd); >+ } else { >+ if (nodeInLayer.order > nodeToAdd.order) >+ layerIterator.previous(); >+ layerIterator.add(nodeToAdd); >+ } >+ layerIterator.previous(); >+ } >+ // move the rest of nodes so that they don't overlap >+ while (layerIterator.hasNext()) { >+ SpaceTreeNode nodeInLayer = (SpaceTreeNode) layerIterator.next(); >+ double expectedPostion = (previousNode == null) ? 0 : previousNode.positionInLayer + expectedDistance(previousNode, nodeInLayer); >+ nodeInLayer.positionInLayer = Math.max(nodeInLayer.positionInLayer, expectedPostion); >+ previousNode = nodeInLayer; >+ } >+ >+ refreshThickness(); >+ } >+ >+ public void removeNode(SpaceTreeNode node) { >+ if (nodes.remove(node)) { >+ ((SpaceTreeLayer) spaceTreeLayers.get(depth + 1)).removeNodes(node.children); >+ refreshThickness(); >+ } >+ } >+ >+ public void removeNodes(List nodesToRemove) { >+ if (this.nodes.removeAll(nodesToRemove)) { >+ SpaceTreeLayer nextLayer = ((SpaceTreeLayer) spaceTreeLayers.get(depth + 1)); >+ for (Iterator iterator = nodesToRemove.iterator(); iterator.hasNext();) { >+ SpaceTreeNode nodeToRemove = (SpaceTreeNode) iterator.next(); >+ nextLayer.removeNodes(nodeToRemove.children); >+ } >+ refreshThickness(); >+ } >+ } >+ >+ public void checkThickness(SpaceTreeNode node) { >+ double nodeThickness = 0; >+ DisplayIndependentDimension size = node.node.getSize(); >+ nodeThickness = (direction == TOP_DOWN || direction == BOTTOM_UP) ? size.height : size.width; >+ if (node.subgraph != null && node.subgraph.isGraphEntity()) { >+ size = node.subgraph.getSize(); >+ nodeThickness += (direction == TOP_DOWN || direction == BOTTOM_UP) ? size.height : size.width; >+ } >+ this.thickness = Math.max(this.thickness, nodeThickness); >+ } >+ >+ public void refreshThickness() { >+ this.thickness = 0; >+ for (Iterator iterator = nodes.iterator(); iterator.hasNext();) { >+ checkThickness((SpaceTreeNode) iterator.next()); >+ } >+ } >+ >+ public void fitNodesWithinBounds(List nodeList, double startPosition, double endPosition) { >+ NodeSnapshot[][] snapShot = takeSnapShot(); >+ SpaceTreeNode[] nodes = (SpaceTreeNode[]) nodeList.toArray(new SpaceTreeNode[nodeList.size()]); >+ double initialStartPosition = nodes[0].positionInLayer; >+ double initialNodesBredth = nodes[nodes.length - 1].positionInLayer - initialStartPosition; >+ double[] desiredPositions = new double[nodes.length]; >+ // calculate desired positions for every node, regarding their >+ // initial initial proportions >+ for (int i = 0; i < nodes.length; i++) { >+ double initialPositionAsPercent = (initialNodesBredth > 0) ? (nodes[i].positionInLayer - initialStartPosition) / initialNodesBredth >+ : 0; >+ desiredPositions[i] = initialPositionAsPercent * (endPosition - startPosition); >+ } >+ // make sure there's proper distance between each pair of >+ // consecutive nodes >+ for (int i = 1; i < nodes.length; i++) { >+ SpaceTreeNode node = nodes[i]; >+ SpaceTreeNode previousNode = nodes[i - 1]; >+ double expectedDistance = expectedDistance(previousNode, node); >+ if (desiredPositions[i] - desiredPositions[i - 1] < expectedDistance) { >+ desiredPositions[i] = desiredPositions[i - 1] + expectedDistance; >+ } >+ } >+ // if the above operation caused some nodes to fall out of requested >+ // bounds, push them back >+ if (desiredPositions[nodes.length - 1] > (endPosition - startPosition)) { >+ desiredPositions[nodes.length - 1] = (endPosition - startPosition); >+ for (int i = nodes.length - 1; i > 0; i--) { >+ SpaceTreeNode node = nodes[i]; >+ SpaceTreeNode previousNode = nodes[i - 1]; >+ double expectedDistance = expectedDistance(previousNode, node); >+ if (desiredPositions[i] - desiredPositions[i - 1] < expectedDistance) { >+ desiredPositions[i - 1] = desiredPositions[i] - expectedDistance; >+ } else >+ break; >+ } >+ } >+ >+ for (int i = 0; i < nodeList.size(); i++) { >+ SpaceTreeNode node = (SpaceTreeNode) nodeList.get(i); >+ double desiredPosition = startPosition + desiredPositions[i]; >+ moveNode(node, desiredPosition); >+ if (Math.abs(node.positionInLayer - desiredPosition) > 0.5) { >+ startPosition += (node.positionInLayer - desiredPosition); >+ i = -1; >+ revertToShanpshot(snapShot); >+ } >+ } >+ } >+ >+ public void moveNode(SpaceTreeNode node, double newPosition) { >+ Collections.sort(nodes, new Comparator() { >+ public int compare(Object arg0, Object arg1) { >+ return ((SpaceTreeNode) arg0).order - ((SpaceTreeNode) arg1).order; >+ } >+ }); >+ double positionInLayerAtStart = node.positionInLayer; >+ if (newPosition >= positionInLayerAtStart) >+ moveNodeForward(node, newPosition); >+ if (newPosition <= positionInLayerAtStart) >+ moveNodeBackward(node, newPosition); >+ } >+ >+ /** >+ * Tries to increase node's position in layer. It can move a node only >+ * if it doesn't cause nodes to fall out of available space (see >+ * {@link SpaceTreeLayoutAlgorithm#getAvailableSpace()}. If there's not >+ * enough space available, some nodes may be collapsed to increase it as >+ * long as it doesn't cause >+ * {@link SpaceTreeLayoutAlgorithm#protectedNode} or any of its >+ * descendants to be collapsed. >+ * >+ * @param nodeToMove >+ * @param newPosition >+ */ >+ private void moveNodeForward(SpaceTreeNode nodeToMove, double newPosition) { >+ int nodeIndex = nodes.indexOf(nodeToMove); >+ if (nodeIndex == -1) >+ throw new IllegalArgumentException("node not on this layer"); >+ // move forward -> check space to the 'right' >+ NodeSnapshot[][] snapShot = takeSnapShot(); >+ boolean firstRun = true; >+ mainLoop: while (firstRun || nodeToMove.positionInLayer < newPosition) { >+ firstRun = false; >+ double requiredSpace = 0; >+ SpaceTreeNode previousNode = nodeToMove; >+ for (int i = nodeIndex + 1; i < nodes.size(); i++) { >+ SpaceTreeNode nextNode = (SpaceTreeNode) nodes.get(i); >+ requiredSpace += expectedDistance(previousNode, nextNode); >+ previousNode = nextNode; >+ } >+ requiredSpace += previousNode.spaceRequiredForNode() / 2; >+ if (requiredSpace > getAvailableSpace() - newPosition) { >+ // find nodes to remove >+ boolean removed = false; >+ for (int i = nodeIndex; i < nodes.size(); i++) { >+ SpaceTreeNode nextNode = ((SpaceTreeNode) nodes.get(i)); >+ if (protectedNode == null || (!protectedNode.isAncestorOf(nextNode) && !nextNode.parent.isAncestorOf(protectedNode))) { >+ collapseNode((SpaceTreeNode) nextNode.parent); >+ if (nextNode.parent == nodeToMove.parent) >+ break mainLoop; >+ removed = true; >+ break; >+ } >+ } >+ if (!removed) { >+ // not enough space, but we can't collapse anything... >+ newPosition = getAvailableSpace() - requiredSpace; >+ revertToShanpshot(snapShot); >+ continue mainLoop; >+ } >+ } >+ // move the node and all its neighbors to the 'right' >+ SpaceTreeNode currentNodeToMove = nodeToMove; >+ double newPositionForCurrent = newPosition; >+ for (int i = nodeIndex; i < nodes.size(); i++) { >+ currentNodeToMove.positionInLayer = newPositionForCurrent; >+ // move parent if moved node is its first child >+ if (currentNodeToMove.firstChild) { >+ SpaceTreeNode parent = (SpaceTreeNode) currentNodeToMove.parent; >+ if (depth > 0 && parent.positionInLayer <= newPositionForCurrent) { >+ SpaceTreeLayer parentLayer = (SpaceTreeLayer) spaceTreeLayers.get(depth - 1); >+ parentLayer.moveNodeForward(parent, newPositionForCurrent); >+ if (parent.positionInLayer < newPositionForCurrent) { >+ double delta = newPositionForCurrent - parent.positionInLayer; >+ newPosition -= delta; >+ revertToShanpshot(snapShot); >+ continue mainLoop; >+ } >+ } >+ } >+ // move children if necessary >+ if (currentNodeToMove.expanded && !currentNodeToMove.children.isEmpty()) { >+ SpaceTreeNode lastChild = (SpaceTreeNode) currentNodeToMove.children.get(currentNodeToMove.children.size() - 1); >+ if (lastChild.positionInLayer < newPositionForCurrent) { >+ // try to move all the children, that is move the >+ // first child and the rest will be pushed >+ SpaceTreeNode firstChild = (SpaceTreeNode) currentNodeToMove.children.get(0); >+ SpaceTreeLayer childLayer = (SpaceTreeLayer) spaceTreeLayers.get(depth + 1); >+ double expectedDistanceBetweenChildren = currentNodeToMove.spaceRequiredForChildren() - firstChild.spaceRequiredForNode() >+ / 2 - lastChild.spaceRequiredForNode() / 2; >+ childLayer.moveNodeForward(firstChild, newPositionForCurrent - expectedDistanceBetweenChildren); >+ if (currentNodeToMove.expanded && lastChild.positionInLayer < newPositionForCurrent) { >+ // the previous attempt failed -> try to move >+ // only the last child >+ childLayer.moveNodeForward(lastChild, newPositionForCurrent); >+ if (lastChild.positionInLayer < newPositionForCurrent) { >+ // child couldn't be moved as far as needed >+ // -> move current node back to the position >+ // over the child >+ double delta = newPositionForCurrent - lastChild.positionInLayer; >+ newPosition -= delta; >+ revertToShanpshot(snapShot); >+ continue mainLoop; >+ } >+ } >+ } >+ } >+ >+ if (i < nodes.size() - 1) { >+ SpaceTreeNode nextNode = (SpaceTreeNode) nodes.get(i + 1); >+ newPositionForCurrent += expectedDistance(currentNodeToMove, nextNode); >+ currentNodeToMove = nextNode; >+ if (currentNodeToMove.positionInLayer > newPositionForCurrent) >+ break; >+ } >+ } >+ } >+ } >+ >+ /** >+ * Method complementary to >+ * {@link #moveNodeForward(SpaceTreeNode, double)}. Decreases node's >+ * position in layer. >+ * >+ * @param nodeToMove >+ * @param newPosition >+ */ >+ private void moveNodeBackward(SpaceTreeNode nodeToMove, double newPosition) { >+ int nodeIndex = nodes.indexOf(nodeToMove); >+ if (nodeIndex == -1) >+ throw new IllegalArgumentException("node not on this layer"); >+ // move backward -> check space to the 'left' >+ // move and collapse until there's enough space >+ NodeSnapshot[][] snapShot = takeSnapShot(); >+ boolean firstRun = true; >+ mainLoop: while (firstRun || nodeToMove.positionInLayer > newPosition) { >+ firstRun = false; >+ double requiredSpace = 0; >+ SpaceTreeNode previousNode = nodeToMove; >+ for (int i = nodeIndex - 1; i >= 0; i--) { >+ SpaceTreeNode nextNode = (SpaceTreeNode) nodes.get(i); >+ requiredSpace += expectedDistance(previousNode, nextNode); >+ previousNode = nextNode; >+ } >+ requiredSpace += previousNode.spaceRequiredForNode() / 2; >+ if (requiredSpace > newPosition) { >+ // find nodes to remove >+ boolean removed = false; >+ for (int i = nodeIndex; i >= 0; i--) { >+ SpaceTreeNode nextNode = ((SpaceTreeNode) nodes.get(i)); >+ if (protectedNode == null || (!protectedNode.isAncestorOf(nextNode) && !nextNode.parent.isAncestorOf(protectedNode))) { >+ collapseNode((SpaceTreeNode) nextNode.parent); >+ if (nextNode.parent == nodeToMove.parent) >+ break mainLoop; >+ nodeIndex -= nextNode.parent.children.size(); >+ removed = true; >+ break; >+ } >+ } >+ if (!removed) { >+ // not enough space, but we can't collapse anything... >+ newPosition = requiredSpace; >+ revertToShanpshot(snapShot); >+ continue mainLoop; >+ } >+ } >+ // move the node and all its neighbors to the 'left' >+ SpaceTreeNode currentNodeToMove = nodeToMove; >+ double newPositionForCurrent = newPosition; >+ for (int i = nodeIndex; i >= 0; i--) { >+ currentNodeToMove.positionInLayer = newPositionForCurrent; >+ // move parent if moved node is its last child >+ if (currentNodeToMove.lastChild) { >+ SpaceTreeNode parent = (SpaceTreeNode) currentNodeToMove.parent; >+ if (depth > 0 && parent.positionInLayer >= newPositionForCurrent) { >+ SpaceTreeLayer parentLayer = (SpaceTreeLayer) spaceTreeLayers.get(depth - 1); >+ parentLayer.moveNodeBackward(parent, newPositionForCurrent); >+ if (parent.positionInLayer > newPositionForCurrent) { >+ double delta = parent.positionInLayer - newPositionForCurrent; >+ newPosition += delta; >+ revertToShanpshot(snapShot); >+ continue mainLoop; >+ } >+ } >+ } >+ // move children if necessary >+ if (currentNodeToMove.expanded && !currentNodeToMove.children.isEmpty()) { >+ SpaceTreeNode firstChild = (SpaceTreeNode) currentNodeToMove.children.get(0); >+ if (firstChild.positionInLayer > newPositionForCurrent) { >+ // try to move all the children, that is move the >+ // last child and the rest will be pushed >+ SpaceTreeNode lastChild = (SpaceTreeNode) currentNodeToMove.children.get(currentNodeToMove.children.size() - 1); >+ SpaceTreeLayer childLayer = (SpaceTreeLayer) spaceTreeLayers.get(depth + 1); >+ double expectedDistanceBetweenChildren = currentNodeToMove.spaceRequiredForChildren() - firstChild.spaceRequiredForNode() >+ / 2 - lastChild.spaceRequiredForNode() / 2; >+ childLayer.moveNodeBackward(lastChild, newPositionForCurrent + expectedDistanceBetweenChildren); >+ if (currentNodeToMove.expanded && firstChild.positionInLayer > newPositionForCurrent) { >+ // the previous attempt failed -> try to move >+ // only the first child >+ childLayer.moveNodeBackward(firstChild, newPositionForCurrent); >+ if (firstChild.positionInLayer > newPositionForCurrent) { >+ // child couldn't be moved as far as needed >+ // -> move current node back to the position >+ // over the child >+ double delta = firstChild.positionInLayer - newPositionForCurrent; >+ newPosition += delta; >+ revertToShanpshot(snapShot); >+ continue mainLoop; >+ } >+ } >+ } >+ } >+ if (i > 0) { >+ SpaceTreeNode nextNode = (SpaceTreeNode) nodes.get(i - 1); >+ newPositionForCurrent -= expectedDistance(currentNodeToMove, nextNode); >+ currentNodeToMove = nextNode; >+ if (currentNodeToMove.positionInLayer < newPositionForCurrent) >+ break; >+ } >+ } >+ } >+ } >+ >+ public String toString() { >+ StringBuffer buffer = new StringBuffer(); >+ buffer.append("Layer ").append(depth).append(": "); >+ for (Iterator iterator = nodes.iterator(); iterator.hasNext();) { >+ SpaceTreeNode node = (SpaceTreeNode) iterator.next(); >+ buffer.append(node.node).append(", "); >+ } >+ return buffer.toString(); >+ } >+ >+ private void collapseNode(SpaceTreeNode node) { >+ node.expanded = false; >+ SpaceTreeLayer layer = (SpaceTreeLayer) spaceTreeLayers.get(node.depth + 1); >+ layer.removeNodes(node.children); >+ for (Iterator iterator = node.children.iterator(); iterator.hasNext();) { >+ SpaceTreeNode child = (SpaceTreeNode) iterator.next(); >+ if (child.expanded) >+ collapseNode(child); >+ } >+ } >+ } >+ >+ private class SpaceTreeExpandCollapseManager implements ExpandCollapseManager { >+ public void initExpansion(LayoutContext context) { >+ // do nothing - initialization performed in #setLayoutContext() >+ } >+ >+ public void setExpanded(LayoutContext context, NodeLayout node, boolean expanded) { >+ SpaceTreeNode spaceTreeNode = (SpaceTreeNode) treeObserver.getTreeNode(node); >+ if (expanded) { >+ maximizeExpansion(spaceTreeNode); >+ refreshLayout(true); >+ } else if (spaceTreeNode.expanded) { >+ spaceTreeNode.expanded = false; >+ ((SpaceTreeLayer) spaceTreeLayers.get(spaceTreeNode.depth + 1)).removeNodes(spaceTreeNode.children); >+ refreshLayout(true); >+ } >+ } >+ >+ public boolean canExpand(LayoutContext context, NodeLayout node) { >+ SpaceTreeNode spaceTreeNode = (SpaceTreeNode) treeObserver.getTreeNode(node); >+ if (spaceTreeNode != null) { >+ // we don't check if node is expanded because it can be expanded >+ // again >+ return !spaceTreeNode.children.isEmpty(); >+ } >+ return false; >+ } >+ >+ public boolean canCollapse(LayoutContext context, NodeLayout node) { >+ SpaceTreeNode spaceTreeNode = (SpaceTreeNode) treeObserver.getTreeNode(node); >+ if (spaceTreeNode != null) { >+ return spaceTreeNode.expanded && !spaceTreeNode.children.isEmpty(); >+ } >+ return false; >+ } >+ >+ public void maximizeExpansion(SpaceTreeNode nodeToExpand) { >+ protectedNode = nodeToExpand; >+ double availableSpace = getAvailableSpace(); >+ double requiredSpace = 0; >+ >+ ((SpaceTreeLayer) spaceTreeLayers.get(nodeToExpand.depth + 1)).removeNodes(nodeToExpand.children); >+ >+ ArrayList nodesInThisLayer = null; >+ ArrayList nodesInNextLayer = new ArrayList(); >+ nodesInNextLayer.add(nodeToExpand); >+ double spaceRequiredInNextLayer = nodeToExpand.spaceRequiredForNode(); >+ for (int layer = 0; !nodesInNextLayer.isEmpty(); layer++) { >+ NodeSnapshot[][] snapShot = takeSnapShot(); >+ requiredSpace = Math.max(requiredSpace, spaceRequiredInNextLayer); >+ spaceRequiredInNextLayer = 0; >+ >+ nodesInThisLayer = nodesInNextLayer; >+ nodesInNextLayer = new ArrayList(); >+ >+ int numOfNodesWithChildren = 0; >+ for (Iterator iterator = nodesInThisLayer.iterator(); iterator.hasNext();) { >+ SpaceTreeNode node = (SpaceTreeNode) iterator.next(); >+ if (!node.children.isEmpty()) { >+ node.expanded = true; >+ spaceRequiredInNextLayer += node.spaceRequiredForChildren(); >+ nodesInNextLayer.addAll(node.children); >+ numOfNodesWithChildren++; >+ } >+ } >+ >+ for (Iterator iterator = nodesInNextLayer.iterator(); iterator.hasNext();) { >+ SpaceTreeNode node = (SpaceTreeNode) iterator.next(); >+ node.expanded = false; >+ } >+ >+ if (numOfNodesWithChildren == 0) >+ break; >+ >+ spaceRequiredInNextLayer += branchGap * (numOfNodesWithChildren - 1); >+ >+ boolean addedNewLayer = false; >+ if ((spaceRequiredInNextLayer <= requiredSpace || spaceRequiredInNextLayer <= availableSpace || (layer < 1 && nodeToExpand.depth >+ + layer < 1)) >+ && !nodesInNextLayer.isEmpty()) { >+ // add next layer and center its nodes >+ >+ SpaceTreeLayer childLayer = (SpaceTreeLayer) spaceTreeLayers.get(nodeToExpand.depth + layer + 1); >+ childLayer.addNodes(nodesInNextLayer); >+ SpaceTreeNode firstChild = ((SpaceTreeNode) nodesInNextLayer.get(0)); >+ SpaceTreeNode lastChild = ((SpaceTreeNode) nodesInNextLayer.get(nodesInNextLayer.size() - 1)); >+ double boundsWidth = spaceRequiredInNextLayer - firstChild.spaceRequiredForNode() / 2 - lastChild.spaceRequiredForNode() / 2; >+ double startPosition = Math.max((availableSpace - boundsWidth) / 2, firstChild.spaceRequiredForNode() / 2); >+ setAvailableSpace(spaceRequiredInNextLayer); >+ childLayer.fitNodesWithinBounds(nodesInNextLayer, startPosition, startPosition + boundsWidth); >+ setAvailableSpace(0); >+ if (nodeToExpand.childrenPositionsOK(nodesInThisLayer) || layer == 0 || nodeToExpand.depth + layer < 1) >+ addedNewLayer = true; >+ } >+ if (!addedNewLayer) { >+ revertToShanpshot(snapShot); >+ break; >+ } >+ } >+ nodeToExpand.centerParentsBottomUp(); >+ nodeToExpand.centerParentsTopDown(); >+ } >+ }; >+ >+ private SpaceTreeExpandCollapseManager expandCollapseManager = new SpaceTreeExpandCollapseManager(); >+ >+ private ContextListener contextListener = new ContextListener.Stub() { >+ public boolean boundsChanged(LayoutContext context) { >+ boolean previousBoundsWrong = (bounds == null || bounds.width * bounds.height <= 0); >+ bounds = context.getBounds(); >+ if (bounds.width * bounds.height > 0 && previousBoundsWrong) { >+ expandCollapseManager.maximizeExpansion((SpaceTreeNode) treeObserver.getSuperRoot()); >+ refreshLayout(false); >+ } >+ return false; >+ } >+ }; >+ >+ private LayoutListener layoutListener = new LayoutListener() { >+ >+ public boolean subgraphResized(LayoutContext context, SubgraphLayout subgraph) { >+ return defaultSubgraphHandle(context, subgraph); >+ } >+ >+ public boolean subgraphMoved(LayoutContext context, SubgraphLayout subgraph) { >+ return defaultSubgraphHandle(context, subgraph); >+ } >+ >+ public boolean nodeResized(LayoutContext context, NodeLayout node) { >+ setAvailableSpace(getAvailableSpace() + ((SpaceTreeNode) treeObserver.getTreeNode(node)).spaceRequiredForNode()); >+ boolean result = defaultNodeHandle(context, node); >+ setAvailableSpace(0); >+ return result; >+ } >+ >+ public boolean nodeMoved(LayoutContext context, NodeLayout node) { >+ return defaultNodeHandle(context, node); >+ } >+ >+ /** >+ * Finds a root of given subgraph, updates the root position and moves >+ * the subgraph to proper position >+ * >+ * @param context >+ * @param subgraph >+ * @return >+ */ >+ private boolean defaultSubgraphHandle(LayoutContext context, SubgraphLayout subgraph) { >+ SpaceTreeNode node = (SpaceTreeNode) treeObserver.getTreeNode(subgraph.getNodes()[0]); >+ while (node != null && node.node != null && node.node.getSubgraph() == subgraph) { >+ node = (SpaceTreeNode) node.parent; >+ } >+ if (node != null && node.subgraph == subgraph) { >+ node.adjustPosition(subgraph.getLocation()); >+ if (context.isBackgroundLayoutEnabled()) { >+ ((SpaceTreeNode) treeObserver.getSuperRoot()).flushLocationChanges(0); >+ node.refreshSubgraphLocation(); >+ context.flushChanges(false); >+ } >+ } >+ return false; >+ } >+ >+ private boolean defaultNodeHandle(LayoutContext context, NodeLayout node) { >+ if (bounds.width * bounds.height <= 0) >+ return false; >+ SpaceTreeNode spaceTreeNode = (SpaceTreeNode) treeObserver.getTreeNode(node); >+ spaceTreeNode.adjustPosition(node.getLocation()); >+ if (context.isBackgroundLayoutEnabled()) { >+ ((SpaceTreeNode) treeObserver.getSuperRoot()).flushLocationChanges(0); >+ spaceTreeNode.refreshSubgraphLocation(); >+ context.flushChanges(false); >+ } >+ return false; >+ } >+ }; >+ >+ private int direction = TOP_DOWN; >+ >+ private double leafGap = 15; >+ private double branchGap = leafGap + 5; >+ private double layerGap = 20; >+ >+ private boolean directionChanged = false; >+ >+ private LayoutContext context; >+ >+ private DisplayIndependentRectangle bounds; >+ >+ private TreeLayoutObserver treeObserver; >+ >+ private double availableSpace; >+ >+ private ArrayList spaceTreeLayers = new ArrayList(); >+ >+ /** >+ * If not null, this node and all of its children shall not be collapsed >+ * during node movements. >+ */ >+ private SpaceTreeNode protectedNode = null; >+ >+ /** >+ * Constructs an instance of <code>SpaceTreeLayoutAlgorithm</code> that >+ * places the root of a tree at the top of the graph. >+ */ >+ public SpaceTreeLayoutAlgorithm() { >+ } >+ >+ /** >+ * Constructs an instance of <code>SpaceTreeLayoutAlgorithm</code> that >+ * places the root of a tree according to given direction >+ * >+ * @param direction >+ * direction of the tree, sould be one of the following: >+ * {@link #TOP_DOWN}, {@link #BOTTOM_UP}, {@link #LEFT_RIGHT}, >+ * {@link #RIGHT_LEFT}. >+ */ >+ public SpaceTreeLayoutAlgorithm(int direction) { >+ setDirection(direction); >+ } >+ >+ /** >+ * >+ * @return current direction (placement) of the tree >+ */ >+ public int getDirection() { >+ return direction; >+ } >+ >+ /** >+ * Sets direction (placement) of the tree >+ * >+ * @param direction >+ * direction of the tree, sould be one of the following: >+ * {@link #TOP_DOWN}, {@link #BOTTOM_UP}, {@link #LEFT_RIGHT}, >+ * {@link #RIGHT_LEFT}. >+ */ >+ public void setDirection(int direction) { >+ if (direction == this.direction) >+ return; >+ if (direction == TOP_DOWN || direction == BOTTOM_UP || direction == LEFT_RIGHT || direction == RIGHT_LEFT) { >+ this.direction = direction; >+ directionChanged = true; >+ if (context.isBackgroundLayoutEnabled()) >+ checkPendingChangeDirection(); >+ } else >+ throw new IllegalArgumentException("Invalid direction: " + direction); >+ } >+ >+ public void applyLayout(boolean clean) { >+ bounds = context.getBounds(); >+ >+ if (bounds.width * bounds.height == 0) >+ return; >+ >+ if (clean) { >+ treeObserver.recomputeTree(); >+ expandCollapseManager.maximizeExpansion((SpaceTreeNode) treeObserver.getSuperRoot()); >+ } >+ SpaceTreeNode superRoot = ((SpaceTreeNode) treeObserver.getSuperRoot()); >+ superRoot.flushExpansionChanges(); >+ superRoot.flushLocationChanges(0); >+ checkPendingChangeDirection(); >+ } >+ >+ public void setLayoutContext(LayoutContext context) { >+ if (this.context != null) { >+ this.context.removeContextListener(contextListener); >+ this.context.removeLayoutListener(layoutListener); >+ treeObserver.stop(); >+ } >+ this.context = context; >+ context.addContextListener(contextListener); >+ context.addLayoutListener(layoutListener); >+ treeObserver = new TreeLayoutObserver(context, spaceTreeNodeFactory); >+ >+ bounds = context.getBounds(); >+ } >+ >+ /** >+ * >+ * @return <code>ExpandCollapseManager</code> that can (and should) be used >+ * on layout context managed by this layout algorithm. >+ */ >+ public ExpandCollapseManager getExpandCollapseManager() { >+ return expandCollapseManager; >+ } >+ >+ private void checkPendingChangeDirection() { >+ if (directionChanged) { >+ SubgraphLayout[] subgraphs = context.getSubgraphs(); >+ int subgraphDirection = getSubgraphDirection(); >+ for (int i = 0; i < subgraphs.length; i++) { >+ subgraphs[i].setDirection(subgraphDirection); >+ } >+ directionChanged = false; >+ } >+ } >+ >+ private int getSubgraphDirection() { >+ switch (direction) { >+ case TOP_DOWN: >+ return SubgraphLayout.TOP_DOWN; >+ case BOTTOM_UP: >+ return SubgraphLayout.BOTTOM_UP; >+ case LEFT_RIGHT: >+ return SubgraphLayout.LEFT_RIGHT; >+ case RIGHT_LEFT: >+ return SubgraphLayout.RIGHT_LEFT; >+ } >+ throw new RuntimeException(); >+ } >+ >+ protected void refreshLayout(boolean animation) { >+ if (!context.isBackgroundLayoutEnabled()) >+ return; >+ SpaceTreeNode superRoot = (SpaceTreeNode) treeObserver.getSuperRoot(); >+ if (animation && superRoot.flushCollapseChanges()) >+ context.flushChanges(true); >+ if (superRoot.flushLocationChanges(0) && animation) >+ context.flushChanges(true); >+ superRoot.flushExpansionChanges(); >+ superRoot.flushLocationChanges(0); >+ context.flushChanges(animation); >+ } >+ >+ /** >+ * Available space is the biggest of the following values: >+ * <ul> >+ * <li>Space provided by current context bounds</li> >+ * <li>Space already taken by the widest layer</li> >+ * <li>Value set with {@link #setAvailableSpace(double)}</li> >+ * </ul> >+ * >+ * @return >+ */ >+ private double getAvailableSpace() { >+ double result = (direction == TOP_DOWN || direction == BOTTOM_UP) ? bounds.width : bounds.height; >+ result = Math.max(result, this.availableSpace); >+ for (Iterator iterator = spaceTreeLayers.iterator(); iterator.hasNext();) { >+ SpaceTreeLayer layer = (SpaceTreeLayer) iterator.next(); >+ if (!layer.nodes.isEmpty()) { >+ SpaceTreeNode first = (SpaceTreeNode) layer.nodes.get(0); >+ SpaceTreeNode last = (SpaceTreeNode) layer.nodes.get(layer.nodes.size() - 1); >+ result = Math.max(result, last.positionInLayer - first.positionInLayer + (first.spaceRequiredForNode() + last.spaceRequiredForNode()) >+ / 2); >+ } else >+ break; >+ } >+ return result; >+ } >+ >+ /** >+ * This method allows to reserve more space than actual layout bounds >+ * provide or nodes currently occupy. >+ * >+ * @param availableSpace >+ */ >+ private void setAvailableSpace(double availableSpace) { >+ this.availableSpace = availableSpace; >+ } >+ >+ private double expectedDistance(SpaceTreeNode node, SpaceTreeNode neighbor) { >+ double expectedDistance = (node.spaceRequiredForNode() + neighbor.spaceRequiredForNode()) / 2; >+ expectedDistance += (node.parent == neighbor.parent) ? leafGap : branchGap; >+ return expectedDistance; >+ } >+ >+ private class NodeSnapshot { >+ SpaceTreeNode node; >+ double position; >+ boolean expanded; >+ } >+ >+ /** >+ * Stores current expansion state of tree nodes and their position in layers >+ * >+ * @return array containing state of all unpruned nodes >+ */ >+ private NodeSnapshot[][] takeSnapShot() { >+ NodeSnapshot[][] result = new NodeSnapshot[spaceTreeLayers.size()][]; >+ for (int i = 0; i < result.length; i++) { >+ SpaceTreeLayer layer = (SpaceTreeLayer) spaceTreeLayers.get(i); >+ result[i] = new NodeSnapshot[layer.nodes.size()]; >+ for (int j = 0; j < result[i].length; j++) { >+ result[i][j] = new NodeSnapshot(); >+ result[i][j].node = ((SpaceTreeNode) layer.nodes.get(j)); >+ result[i][j].position = result[i][j].node.positionInLayer; >+ result[i][j].expanded = result[i][j].node.expanded; >+ } >+ } >+ return result; >+ } >+ >+ /** >+ * Restores tree nodes' expansion state and position in layers >+ * >+ * @param snapShot >+ * state obtained with {@link #takeSnapShot()} >+ */ >+ private void revertToShanpshot(NodeSnapshot[][] snapShot) { >+ for (int i = 0; i < snapShot.length; i++) { >+ SpaceTreeLayer layer = (SpaceTreeLayer) spaceTreeLayers.get(i); >+ layer.nodes.clear(); >+ for (int j = 0; j < snapShot[i].length; j++) { >+ snapShot[i][j].node.positionInLayer = snapShot[i][j].position; >+ snapShot[i][j].node.expanded = snapShot[i][j].expanded; >+ layer.nodes.add(snapShot[i][j].node); >+ } >+ } >+ } >+} >Index: src/org/eclipse/zest/layouts/algorithms/SpringLayoutAlgorithm.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.layouts/src/org/eclipse/zest/layouts/algorithms/SpringLayoutAlgorithm.java,v >retrieving revision 1.12 >diff -u -r1.12 SpringLayoutAlgorithm.java >--- src/org/eclipse/zest/layouts/algorithms/SpringLayoutAlgorithm.java 12 Sep 2007 20:44:37 -0000 1.12 >+++ src/org/eclipse/zest/layouts/algorithms/SpringLayoutAlgorithm.java 15 Mar 2010 03:49:08 -0000 >@@ -1,808 +1,776 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts.algorithms; >- >-import java.util.Date; >-import java.util.HashMap; >-import java.util.Map; >- >-import org.eclipse.zest.layouts.LayoutStyles; >-import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle; >-import org.eclipse.zest.layouts.dataStructures.InternalNode; >-import org.eclipse.zest.layouts.dataStructures.InternalRelationship; >- >-/** >- * The SpringLayoutAlgorithm has its own data repository and relation >- * repository. A user can populate the repository, specify the layout >- * conditions, do the computation and query the computed results. >- * <p> >- * Instructions for using SpringLayoutAlgorithm: <br> >- * 1. Instantiate a SpringLayout object; <br> >- * 2. Populate the data repository using {@link #add add(...)}; <br> >- * 3. Populate the relation repository using >- * {@link #addRelation addRelation(...)}; <br> >- * 4. Execute {@link #compute compute()}; <br> >- * 5. Execute {@link #fitWithinBounds fitWithinBounds(...)}; <br> >- * 6. Query the computed results(node size and node position). >- * >- * @version 2.0 >- * @author Ian Bull >- * @author Casey Best (version 1.0 by Jingwei Wu/Rob Lintern) >- */ >-public class SpringLayoutAlgorithm extends ContinuousLayoutAlgorithm { >- >- private final static boolean DEFAULT_ANCHOR = false; >- >- /** >- * The default value for the spring layout number of interations. >- */ >- public static final int DEFAULT_SPRING_ITERATIONS = 1000; >- >- /** >- * the default value for the time algorithm runs. >- */ >- public static final long MAX_SPRING_TIME = 10000; >- >- /** >- * The default value for positioning nodes randomly. >- */ >- public static final boolean DEFAULT_SPRING_RANDOM = true; >- >- /** >- * The default value for ignoring unconnected nodes. >- */ >- public static final boolean DEFAULT_SPRING_IGNORE_UNCON = true; >- >- /** >- * The default value for separating connected components. >- */ >- public static final boolean DEFAULT_SPRING_SEPARATE_COMPONENTS = true; >- >- /** >- * The default value for the spring layout move-control. >- */ >- public static final double DEFAULT_SPRING_MOVE = 1.0f; >- >- /** >- * The default value for the spring layout strain-control. >- */ >- public static final double DEFAULT_SPRING_STRAIN = 1.0f; >- >- /** >- * The default value for the spring layout length-control. >- */ >- public static final double DEFAULT_SPRING_LENGTH = 1.0f; >- >- /** >- * The default value for the spring layout gravitation-control. >- */ >- public static final double DEFAULT_SPRING_GRAVITATION = 1.0f; >- >- /** >- * The variable can be customized to set the number of iterations used. >- */ >- private static int sprIterations = DEFAULT_SPRING_ITERATIONS; >- >- /** >- * This variable can be customized to set the max number of MS the algorithm >- * should run >- */ >- private static long maxTimeMS = MAX_SPRING_TIME; >- >- /** >- * The variable can be customized to set whether or not the spring layout >- * nodes are positioned randomly before beginning iterations. >- */ >- private static boolean sprRandom = DEFAULT_SPRING_RANDOM; >- >- /** >- * Minimum distance considered between nodes >- */ >- protected static final double MIN_DISTANCE = 0.001d; >- >- /** >- * An arbitrarily small value in mathematics. >- */ >- protected static final double EPSILON = 0.001d; >- >- /** >- * The variable can be customerized to set the spring layout move-control. >- */ >- private static double sprMove = DEFAULT_SPRING_MOVE; >- >- /** >- * The variable can be customized to set the spring layout strain-control. >- */ >- private static double sprStrain = DEFAULT_SPRING_STRAIN; >- >- /** >- * The variable can be customized to set the spring layout length-control. >- */ >- private static double sprLength = DEFAULT_SPRING_LENGTH; >- >- /** >- * The variable can be customized to set the spring layout >- * gravitation-control. >- */ >- private static double sprGravitation = DEFAULT_SPRING_GRAVITATION; >- >- /** >- * The largest movement of all vertices that has occured in the most recent >- * iteration. >- */ >- private double largestMovement = 0; >- >- >- /** >- * Maps a src and dest object to the number of relations between them. Key >- * is src.toString() + dest.toString(), value is an Integer >- */ >- private Map srcDestToNumRelsMap; >- >- /** >- * Maps a src and dest object to the average weight of the relations between >- * them. Key is src.toString() + dest.toString(), value is a Double >- */ >- private Map srcDestToRelsAvgWeightMap; >- >- /** >- * Maps a relationship type to a weight. Key is a string, value is a Double >- */ >- private static Map relTypeToWeightMap = new HashMap(); >- >- private int iteration; >- >- private int[][] srcDestToNumRels; >- >- private double[][] srcDestToRelsAvgWeight; >- >- private double[] tempLocationsX; >- >- private double[] tempLocationsY; >- >- private double[] forcesX; >- >- private double[] forcesY; >- >- private boolean[] anchors; >- >- private DisplayIndependentRectangle bounds = null; >- >- Date date = null; >- >- /** >- * Constructor. >- */ >- public SpringLayoutAlgorithm( int styles ) { >- super( styles ); >- srcDestToNumRelsMap = new HashMap(); >- srcDestToRelsAvgWeightMap = new HashMap(); >- date = new Date(); >- } >- >- >- /** >- * Creates a sprint layout algoirthm with no style >- * >- */ >- public SpringLayoutAlgorithm() { >- this( LayoutStyles.NONE ); >- } >- >- public void setLayoutArea(double x, double y, double width, double height) { >- bounds = new DisplayIndependentRectangle(x,y,width,height); >- } >- >- /** >- * Sets the spring layout move-control. >- * >- * @param move >- * The move-control value. >- */ >- public void setSpringMove(double move) { >- sprMove = move; >- } >- >- /** >- * Returns the move-control value of this SpringLayoutAlgorithm in >- * double presion. >- * >- * @return The move-control value. >- */ >- public double getSpringMove() { >- return sprMove; >- } >- >- /** >- * Sets the spring layout strain-control. >- * >- * @param strain >- * The strain-control value. >- */ >- public void setSpringStrain(double strain) { >- sprStrain = strain; >- } >- >- /** >- * Returns the strain-control value of this SpringLayoutAlgorithm in >- * double presion. >- * >- * @return The strain-control value. >- */ >- public double getSpringStrain() { >- return sprStrain; >- } >- >- /** >- * Sets the spring layout length-control. >- * >- * @param length >- * The length-control value. >- */ >- public void setSpringLength(double length) { >- sprLength = length; >- } >- >- /** >- * Gets the max time this algorithm will run for >- * >- * @return >- */ >- public long getSpringTimeout() { >- return maxTimeMS; >- } >- >- /** >- * Sets the spring timeout >- * >- * @param timeout >- */ >- public void setSpringTimeout(long timeout) { >- maxTimeMS = timeout; >- } >- >- /** >- * Returns the length-control value of this SpringLayoutAlgorithm in >- * double presion. >- * >- * @return The length-control value. >- */ >- public double getSpringLength() { >- return sprLength; >- } >- >- /** >- * Sets the spring layout gravitation-control. >- * >- * @param gravitation >- * The gravitation-control value. >- */ >- public void setSpringGravitation(double gravitation) { >- sprGravitation = gravitation; >- } >- >- /** >- * Returns the gravitation-control value of this SpringLayoutAlgorithm >- * in double presion. >- * >- * @return The gravitation-control value. >- */ >- public double getSpringGravitation() { >- return sprGravitation; >- } >- >- /** >- * Sets the number of iterations to be used. >- * >- * @param gravitation >- * The number of iterations. >- */ >- public void setIterations(int iterations) { >- sprIterations = iterations; >- } >- >- /** >- * Returns the number of iterations to be used. >- * >- * @return The number of iterations. >- */ >- public int getIterations() { >- return sprIterations; >- } >- >- /** >- * Sets whether or not this SpringLayoutAlgorithm will layout the >- * nodes randomly before beginning iterations. >- * >- * @param random >- * The random placement value. >- */ >- public void setRandom(boolean random) { >- sprRandom = random; >- } >- >- /** >- * Returns whether or not this SpringLayoutAlgorithm will layout the >- * nodes randomly before beginning iterations. >- */ >- public boolean getRandom() { >- return sprRandom; >- } >- >- public void setWeight(String relType, double weight) { >- relTypeToWeightMap.put(relType, new Double(weight)); >- } >- >- public double getWeight(String relType) { >- Double weight = (Double) relTypeToWeightMap.get(relType); >- return (weight == null) ? 1 : weight.doubleValue(); >- } >- >- /** >- * Sets the default conditions. >- */ >- public void setDefaultConditions() { >- // sprMove = DEFAULT_SPRING_MOVE; >- // sprStrain = DEFAULT_SPRING_STRAIN; >- // sprLength = DEFAULT_SPRING_LENGTH; >- // sprGravitation = DEFAULT_SPRING_GRAVITATION; >- // sprIterations = DEFAULT_SPRING_ITERATIONS; >- } >- >- /** >- * Clean up after done >- * >- * @param entitiesToLayout >- */ >- private void reset(InternalNode[] entitiesToLayout) { >- tempLocationsX = null; >- tempLocationsY = null; >- forcesX = null; >- forcesY = null; >- anchors = null; >- setDefaultConditions(); >- srcDestToNumRelsMap = new HashMap(); >- srcDestToRelsAvgWeightMap = new HashMap(); >- relTypeToWeightMap = new HashMap(); >- } >- >- private long startTime = 0; >- >- protected void preLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height) { >- // TODO: Filter out any non-wanted entities and relationships >- // super.applyLayout(entitiesToLayout, relationshipsToConsider, x, y, >- // width, height); >- //InternalNode[] a_entitiesToLayout = (InternalNode[]) entitiesToLayout.toArray(new InternalNode[entitiesToLayout.size()]); >- bounds = new DisplayIndependentRectangle(x,y,width,height); >- tempLocationsX = new double[entitiesToLayout.length]; >- tempLocationsY = new double[entitiesToLayout.length]; >- forcesX = new double[entitiesToLayout.length]; >- forcesY = new double[entitiesToLayout.length]; >- anchors = new boolean[entitiesToLayout.length]; >- >- for (int i = 0; i < entitiesToLayout.length; i++) { >- anchors[i] = DEFAULT_ANCHOR; >- } >- for (int i = 0; i < relationshipsToConsider.length; i++) { >- InternalRelationship layoutRelationship = relationshipsToConsider[i]; >- addRelation(layoutRelationship); >- } >- >- // do the calculations >- preCompute(entitiesToLayout); >- startTime = date.getTime(); >- } >- >- protected void postLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider) { >- reset(entitiesToLayout); >- } >- >- /** >- * Adds a simple relation between two nodes to the relation repository. >- * >- * @param layoutRelationship >- * The simple relation to be added >- * @throws java.lang.NullPointerExcetption >- * If <code>sr</code> is null >- * @see SimpleRelation >- */ >- private void addRelation(InternalRelationship layoutRelationship) { >- if (layoutRelationship == null) { >- throw new IllegalArgumentException("The arguments can not be null!"); >- } else { >- double weight = layoutRelationship.getWeight(); >- weight = (weight <= 0 ? 0.1 : weight); >- String key1 = layoutRelationship.getSource().toString() + layoutRelationship.getDestination().toString(); >- String key2 = layoutRelationship.getDestination().toString() + layoutRelationship.getSource().toString(); >- String[] keys = { key1, key2 }; >- for (int i = 0; i < keys.length; i++) { >- String key = keys[i]; >- Integer count = (Integer) srcDestToNumRelsMap.get(key); >- Double avgWeight = (Double) srcDestToRelsAvgWeightMap.get(key); >- if (count == null) { >- count = new Integer(1); >- avgWeight = new Double(weight); >- } else { >- int newCount = count.intValue() + 1; >- double newAverage = (avgWeight.doubleValue() * count.doubleValue() + weight) / newCount; >- avgWeight = new Double(newAverage); >- count = new Integer(newCount); >- } >- srcDestToNumRelsMap.put(key, count); >- srcDestToRelsAvgWeightMap.put(key, avgWeight); >- } >- } >- } >- >- private void preCompute(InternalNode [] entitiesToLayout) { >- // count number of relationships between all nodes and the average >- // weight between them >- srcDestToNumRels = new int[entitiesToLayout.length][entitiesToLayout.length]; >- srcDestToRelsAvgWeight = new double[entitiesToLayout.length][entitiesToLayout.length]; >- >- for (int i = 0; i < entitiesToLayout.length - 1; i++) { >- InternalNode layoutEntity1 = entitiesToLayout[i]; >- for (int j = i + 1; j < entitiesToLayout.length; j++) { >- InternalNode layoutEntity2 = entitiesToLayout[j]; >- srcDestToNumRels[i][j] = numRelations(layoutEntity1, layoutEntity2); >- srcDestToNumRels[i][j] += numRelations(layoutEntity2, layoutEntity1); >- srcDestToRelsAvgWeight[i][j] = avgWeight(layoutEntity1, layoutEntity2); >- } >- } >- >- if (sprRandom) >- placeRandomly(entitiesToLayout); // put vertices in random places >- else >- convertToUnitCoordinates(entitiesToLayout); >- >- iteration = 1; >- largestMovement = Double.MAX_VALUE; >- } >- >- // TODO: This is a complete Clone! (and not in a good way) >- protected DisplayIndependentRectangle getLayoutBoundsTemp(InternalNode [] entitiesToLayout, boolean includeNodeSize) { >- double rightSide = Double.MIN_VALUE; >- double bottomSide = Double.MIN_VALUE; >- double leftSide = Double.MAX_VALUE; >- double topSide = Double.MAX_VALUE; >- for (int i = 0; i < entitiesToLayout.length; i++) { >- double x = tempLocationsX[i]; >- double y = tempLocationsY[i]; >- >- leftSide = Math.min(x, leftSide); >- topSide = Math.min(y, topSide); >- rightSide = Math.max(x, rightSide); >- bottomSide = Math.max(y, bottomSide); >- >- } >- return new DisplayIndependentRectangle(leftSide, topSide, rightSide - leftSide, bottomSide - topSide); >- } >- >- protected void convertNodePositionsBack(int i, InternalNode entityToConvert, double px, double py, double screenWidth, double screenHeight, DisplayIndependentRectangle layoutBounds) { >- >- // If the node selected is outside the screen, map it to the boarder >- if ( px > screenWidth ) px = screenWidth; >- if ( py > screenHeight ) py = screenHeight; >- >- if ( px < 0 ) px = 1; >- if ( py < 0 ) py = 1; >- >- double x = (px / screenWidth) * layoutBounds.width + layoutBounds.x; >- double y = (py / screenHeight) * layoutBounds.height + layoutBounds.y; >- >- tempLocationsX[i] = x; >- tempLocationsY[i] = y; >- //setTempLocation(entityToConvert, new DisplayIndependentPoint(x, y)); >- >- if (entityToConvert.getInternalX() < 0) { >- // System.out.println("We have nodes less than 0 here!"); >- } >- >- } >- >- private void checkPreferredLocation(InternalNode [] entitiesToLayout, DisplayIndependentRectangle realBounds) { >- // use 10% for the border - 5% on each side >- double borderWidth = Math.min(realBounds.width, realBounds.height) / 10.0; >- DisplayIndependentRectangle screenBounds = new DisplayIndependentRectangle(realBounds.x + borderWidth / 2.0, realBounds.y + borderWidth / 2.0, realBounds.width - borderWidth, realBounds.height - borderWidth); >- >- DisplayIndependentRectangle layoutBounds = getLayoutBoundsTemp(entitiesToLayout, false); >- for (int i = 0; i < entitiesToLayout.length; i++) { >- InternalNode layoutEntity = entitiesToLayout[i]; >- if (layoutEntity.hasPreferredLocation()) { >- convertNodePositionsBack(i, layoutEntity, layoutEntity.getPreferredX(), layoutEntity.getPreferredY(), screenBounds.width, screenBounds.height, layoutBounds); >- } >- } >- } >- >- /** >- * Scales the current iteration counter based on how long the algorithm has >- * been running for. You can set the MaxTime in maxTimeMS! >- */ >- private void setSprIterationsBasedOnTime() { >- if (maxTimeMS <= 0) >- return; >- >- long currentTime = date.getTime(); >- double fractionComplete = (double) ((double) (currentTime - startTime) / ((double) maxTimeMS)); >- int currentIteration = (int) (fractionComplete * sprIterations); >- if (currentIteration > iteration) { >- iteration = currentIteration; >- } >- >- } >- >- protected boolean performAnotherNonContinuousIteration() { >- setSprIterationsBasedOnTime(); >- if (iteration <= sprIterations && largestMovement >= sprMove) >- return true; >- else >- return false; >- } >- >- protected int getCurrentLayoutStep() { >- return iteration; >- } >- >- protected int getTotalNumberOfLayoutSteps() { >- return sprIterations; >- } >- >- protected void computeOneIteration(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height) { >- if ( bounds == null ) >- bounds = new DisplayIndependentRectangle(x,y,width,height); >- checkPreferredLocation(entitiesToLayout, bounds ); >- computeForces(entitiesToLayout); >- largestMovement = Double.MAX_VALUE; >- computePositions(entitiesToLayout); >- >- for (int i = 0; i < entitiesToLayout.length; i++) { >- InternalNode layoutEntity = entitiesToLayout[i]; >- layoutEntity.setInternalLocation(tempLocationsX[i], tempLocationsY[i]); >- } >- >- defaultFitWithinBounds(entitiesToLayout, bounds); >- >- iteration++; >- } >- >- /** >- * Puts vertices in random places, all between (0,0) and (1,1). >- */ >- public void placeRandomly(InternalNode[] entitiesToLayout) { >- // If only one node in the data repository, put it in the middle >- if (entitiesToLayout.length == 1) { >- // If only one node in the data repository, put it in the middle >- tempLocationsX[0] = 0.5; >- tempLocationsY[0] = 0.5; >- } else { >- for (int i = 0; i < entitiesToLayout.length; i++) { >- if (i == 0) { >- tempLocationsX[i] = 0.0; >- tempLocationsY[i] = 0.0; >- } else if (i == 1) { >- tempLocationsX[i] = 1.0; >- tempLocationsY[i] = 1.0; >- } else { >- tempLocationsX[i] = Math.random(); >- tempLocationsY[i] = Math.random(); >- } >- } >- } >- } >- >- // ///////////////////////////////////////////////////////////////// >- // /// Protected Methods ///// >- // ///////////////////////////////////////////////////////////////// >- >- /** >- * Computes the force for each node in this SpringLayoutAlgorithm. The >- * computed force will be stored in the data repository >- */ >- protected void computeForces(InternalNode[] entitiesToLayout) { >- >- // initialize all forces to zero >- for (int i = 0; i < entitiesToLayout.length; i++) { >- forcesX[i] = 0.0; >- forcesY[i] = 0.0; >- } >- >- // TODO: Again really really slow! >- >- for (int i = 0; i < entitiesToLayout.length - 1; i++) { >- InternalNode sourceEntity = entitiesToLayout[i]; >- >- double srcLocationX = tempLocationsX[i]; >- double srcLocationY = tempLocationsY[i]; >- double fx = forcesX[i]; // force in x direction >- double fy = forcesY[i]; // force in y direction >- >- >- for (int j = i + 1; j < entitiesToLayout.length; j++) { >- InternalNode destinationEntity = entitiesToLayout[j]; >- >- if (!destinationEntity.equals(sourceEntity)) { >- double destLocationX = tempLocationsX[j]; >- double destLocationY = tempLocationsY[j]; >- double dx = srcLocationX - destLocationX; >- double dy = srcLocationY- destLocationY; >- double distance = Math.sqrt(dx * dx + dy * dy); >- double distance_sq = distance * distance; >- // make sure distance and distance squared not too small >- distance = Math.max(MIN_DISTANCE, distance); >- >- // If there are relationships between srcObj and destObj >- // then decrease force on srcObj (a pull) in direction of destObj >- // If no relation between srcObj and destObj then increase >- // force on srcObj (a push) from direction of destObj. >- int numRels = srcDestToNumRels[i][j]; >- double avgWeight = srcDestToRelsAvgWeight[i][j]; >- if (numRels > 0) { >- // nodes are pulled towards each other >- double f = sprStrain * Math.log(distance / sprLength) * numRels * avgWeight; >- >- fx = fx - (f * dx / distance); >- fy = fy - (f * dy / distance); >- >- } else { >- // nodes are repelled from each other >- //double f = Math.min(100, sprGravitation / (distance*distance)); >- double f = sprGravitation / (distance_sq); >- fx = fx + (f * dx / distance); >- fy = fy + (f * dy / distance); >- } >- >- // According to Newton, "for every action, there is an equal >- // and opposite reaction." >- // so give the dest an opposite force >- forcesX[j] = forcesX[j] - fx; >- forcesY[j] = forcesY[j] - fy; >- } >- } >- >- /* >- * //make sure forces aren't too big if (fx > 0 ) fx = Math.min(fx, >- * 10*sprMove); else fx = Math.max(fx, -10*sprMove); if (fy > 0) fy = >- * Math.min(fy, 10*sprMove); else fy = Math.max(fy, -10*sprMove); >- */ >- forcesX[i] = fx; >- forcesY[i] = fy; >- // Remove the src object from the list of destinations since >- // we've already calculated the force from it on all other >- // objects. >- // dests.remove(srcObj); >- >- } >- } >- >- /** >- * Computes the position for each node in this SpringLayoutAlgorithm. >- * The computed position will be stored in the data repository. position = >- * position + sprMove * force >- */ >- protected void computePositions(InternalNode[] entitiesToLayout) { >- for (int i = 0; i < entitiesToLayout.length; i++) { >- if (!anchors[i] || entitiesToLayout[i].hasPreferredLocation() ) { >- double oldX = tempLocationsX[i]; >- double oldY = tempLocationsY[i]; >- double deltaX = sprMove * forcesX[i]; >- double deltaY = sprMove * forcesY[i]; >- >- // constrain movement, so that nodes don't shoot way off to the edge >- double maxMovement = 0.2d * sprMove; >- if (deltaX >= 0) { >- deltaX = Math.min(deltaX, maxMovement); >- } else { >- deltaX = Math.max(deltaX, -maxMovement); >- } >- if (deltaY >= 0) { >- deltaY = Math.min(deltaY, maxMovement); >- } else { >- deltaY = Math.max(deltaY, -maxMovement); >- } >- >- >- largestMovement = Math.max(largestMovement, Math.abs(deltaX)); >- largestMovement = Math.max(largestMovement, Math.abs(deltaY)); >- >- double newX = oldX + deltaX; >- double newY = oldY + deltaY; >- tempLocationsX[i] = newX; >- tempLocationsY[i] = newY; >- } >- >- } >- >- } >- >- /** >- * Converts the position for each node in this SpringLayoutAlgorithm >- * to unit coordinates in double precision. The computed positions will be >- * still stored in the data repository. >- */ >- protected void convertToUnitCoordinates(InternalNode[] entitiesToLayout) { >- double minX = Double.MAX_VALUE; >- double maxX = Double.MIN_VALUE; >- double minY = Double.MAX_VALUE; >- double maxY = Double.MIN_VALUE; >- for (int i = 0; i < entitiesToLayout.length; i++) { >- InternalNode layoutEntity = entitiesToLayout[i]; >- minX = Math.min(minX, layoutEntity.getInternalX()); >- minY = Math.min(minY, layoutEntity.getInternalY()); >- maxX = Math.max(maxX, layoutEntity.getInternalX()); >- maxY = Math.max(maxY, layoutEntity.getInternalY()); >- } >- >- double spanX = maxX - minX; >- double spanY = maxY - minY; >- double maxSpan = Math.max(spanX, spanY); >- >- if (maxSpan > EPSILON) { >- for (int i = 0; i < entitiesToLayout.length; i++) { >- InternalNode layoutEntity = entitiesToLayout[i]; >- double x = (layoutEntity.getInternalX() - minX) / spanX; >- double y = (layoutEntity.getInternalY() - minY) / spanY; >- tempLocationsX[i] = x; >- tempLocationsY[i] = y; >- } >- } else { >- placeRandomly(entitiesToLayout); >- } >- } >- >- /** >- * Examines the number of specified relation between the <code>src</code> >- * and the <code>dest</code> that exist in this >- * SpringLayoutAlgorithm's relation repository. >- * >- * @param src >- * The source part of the relaton to be examined. >- * @param dest >- * The destination part of the relation to be examined. >- * @return The number of relations between src and dest. >- */ >- private int numRelations(Object src, Object dest) { >- String key = src.toString() + dest.toString(); >- Integer count = (Integer) srcDestToNumRelsMap.get(key); >- int intCount = (count == null) ? 0 : count.intValue(); >- return intCount; >- } >- >- /** >- * Returns the average weight between a src and dest object. >- * >- * @param src >- * @param dest >- * @return The average weight between the given src and dest nodes >- */ >- private double avgWeight(Object src, Object dest) { >- String key = src.toString() + dest.toString(); >- Double avgWeight = (Double) srcDestToRelsAvgWeightMap.get(key); >- double doubleWeight = (avgWeight == null) ? 1 : avgWeight.doubleValue(); >- return doubleWeight; >- } >- >- >- protected boolean isValidConfiguration(boolean asynchronous, boolean continueous) { >- if (asynchronous && continueous) >- return true; >- else if (asynchronous && !continueous) >- return true; >- else if (!asynchronous && continueous) >- return false; >- else if (!asynchronous && !continueous) >- return true; >- >- return false; >- } >- >- >-} >- >+/******************************************************************************* >+ * Copyright (c) 2005-2010 The Chisel Group and others. 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 - initial API and implementation >+ * Mateusz Matela >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.layouts.algorithms; >+ >+import java.util.HashMap; >+ >+import org.eclipse.zest.layouts.LayoutAlgorithm; >+import org.eclipse.zest.layouts.dataStructures.DisplayIndependentDimension; >+import org.eclipse.zest.layouts.dataStructures.DisplayIndependentPoint; >+import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle; >+import org.eclipse.zest.layouts.interfaces.ConnectionLayout; >+import org.eclipse.zest.layouts.interfaces.EntityLayout; >+import org.eclipse.zest.layouts.interfaces.LayoutContext; >+import org.eclipse.zest.layouts.interfaces.LayoutListener; >+import org.eclipse.zest.layouts.interfaces.NodeLayout; >+import org.eclipse.zest.layouts.interfaces.SubgraphLayout; >+ >+/** >+ * The SpringLayoutAlgorithm has its own data repository and relation >+ * repository. A user can populate the repository, specify the layout >+ * conditions, do the computation and query the computed results. >+ * <p> >+ * Instructions for using SpringLayoutAlgorithm: <br> >+ * 1. Instantiate a SpringLayout object; <br> >+ * 2. Populate the data repository using {@link #add add(...)}; <br> >+ * 3. Populate the relation repository using {@link #addRelation >+ * addRelation(...)}; <br> >+ * 4. Execute {@link #compute compute()}; <br> >+ * 5. Execute {@link #fitWithinBounds fitWithinBounds(...)}; <br> >+ * 6. Query the computed results(node size and node position). >+ * >+ * @version 2.0 >+ * @author Ian Bull >+ * @author Casey Best (version 1.0 by Jingwei Wu/Rob Lintern) >+ */ >+public class SpringLayoutAlgorithm implements LayoutAlgorithm { >+ >+ /** >+ * The default value for the spring layout number of interations. >+ */ >+ public static final int DEFAULT_SPRING_ITERATIONS = 1000; >+ >+ /** >+ * the default value for the time algorithm runs. >+ */ >+ public static final long MAX_SPRING_TIME = 10000; >+ >+ /** >+ * The default value for positioning nodes randomly. >+ */ >+ public static final boolean DEFAULT_SPRING_RANDOM = true; >+ >+ /** >+ * The default value for the spring layout move-control. >+ */ >+ public static final double DEFAULT_SPRING_MOVE = 1.0f; >+ >+ /** >+ * The default value for the spring layout strain-control. >+ */ >+ public static final double DEFAULT_SPRING_STRAIN = 1.0f; >+ >+ /** >+ * The default value for the spring layout length-control. >+ */ >+ public static final double DEFAULT_SPRING_LENGTH = 3.0f; >+ >+ /** >+ * The default value for the spring layout gravitation-control. >+ */ >+ public static final double DEFAULT_SPRING_GRAVITATION = 2.0f; >+ >+ /** >+ * Minimum distance considered between nodes >+ */ >+ protected static final double MIN_DISTANCE = 1.0d; >+ >+ /** >+ * An arbitrarily small value in mathematics. >+ */ >+ protected static final double EPSILON = 0.001d; >+ >+ /** >+ * The variable can be customized to set the number of iterations used. >+ */ >+ private int sprIterations = DEFAULT_SPRING_ITERATIONS; >+ >+ /** >+ * This variable can be customized to set the max number of MS the algorithm >+ * should run >+ */ >+ private long maxTimeMS = MAX_SPRING_TIME; >+ >+ /** >+ * The variable can be customized to set whether or not the spring layout >+ * nodes are positioned randomly before beginning iterations. >+ */ >+ private boolean sprRandom = DEFAULT_SPRING_RANDOM; >+ >+ /** >+ * The variable can be customized to set the spring layout move-control. >+ */ >+ private double sprMove = DEFAULT_SPRING_MOVE; >+ >+ /** >+ * The variable can be customized to set the spring layout strain-control. >+ */ >+ private double sprStrain = DEFAULT_SPRING_STRAIN; >+ >+ /** >+ * The variable can be customized to set the spring layout length-control. >+ */ >+ private double sprLength = DEFAULT_SPRING_LENGTH; >+ >+ /** >+ * The variable can be customized to set the spring layout >+ * gravitation-control. >+ */ >+ private double sprGravitation = DEFAULT_SPRING_GRAVITATION; >+ >+ /** >+ * Variable indicating whether the algorithm should resize elements. >+ */ >+ private boolean resize = false; >+ >+ private int iteration; >+ >+ private double[][] srcDestToSumOfWeights; >+ >+ private EntityLayout[] entities; >+ >+ private double[] forcesX, forcesY; >+ >+ private double[] locationsX, locationsY; >+ >+ private double[] sizeW, sizeH; >+ >+ private DisplayIndependentRectangle bounds; >+ >+ // private double boundsScale = 0.2; >+ private double boundsScaleX = 0.2; >+ private double boundsScaleY = 0.2; >+ >+ public boolean fitWithinBounds = true; >+ >+ private LayoutContext context; >+ >+ class SpringLayoutListener implements LayoutListener { >+ >+ public boolean nodeMoved(LayoutContext context, NodeLayout node) { >+ // TODO Auto-generated method stub >+ for (int i = 0; i < entities.length; i++) { >+ if (entities[i] == node) { >+ locationsX[i] = entities[i].getLocation().x; >+ locationsY[i] = entities[i].getLocation().y; >+ >+ } >+ >+ } >+ return false; >+ } >+ >+ public boolean nodeResized(LayoutContext context, NodeLayout node) { >+ // TODO Auto-generated method stub >+ return false; >+ } >+ >+ public boolean subgraphMoved(LayoutContext context, SubgraphLayout subgraph) { >+ // TODO Auto-generated method stub >+ return false; >+ } >+ >+ public boolean subgraphResized(LayoutContext context, SubgraphLayout subgraph) { >+ // TODO Auto-generated method stub >+ return false; >+ } >+ >+ } >+ >+ public void applyLayout(boolean clean) { >+ if (!clean) >+ return; >+ while (performAnotherNonContinuousIteration()) { >+ computeOneIteration(); >+ } >+ saveLocations(); >+ if (resize) >+ AlgorithmHelper.maximizeSizes(entities); >+ >+ if (fitWithinBounds) { >+ DisplayIndependentRectangle bounds2 = new DisplayIndependentRectangle(bounds); >+ int insets = 4; >+ bounds2.x += insets; >+ bounds2.y += insets; >+ bounds2.width -= 2 * insets; >+ bounds2.height -= 2 * insets; >+ AlgorithmHelper.fitWithinBounds(entities, bounds2, resize); >+ } >+ >+ } >+ >+ public void setLayoutContext(LayoutContext context) { >+ this.context = context; >+ this.context.addLayoutListener(new SpringLayoutListener()); >+ initLayout(); >+ } >+ >+ public void performNIteration(int n) { >+ if (iteration == 0) { >+ entities = context.getEntities(); >+ loadLocations(); >+ initLayout(); >+ } >+ bounds = context.getBounds(); >+ for (int i = 0; i < n; i++) { >+ computeOneIteration(); >+ saveLocations(); >+ } >+ context.flushChanges(false); >+ } >+ >+ public void performOneIteration() { >+ if (iteration == 0) { >+ entities = context.getEntities(); >+ loadLocations(); >+ initLayout(); >+ } >+ bounds = context.getBounds(); >+ computeOneIteration(); >+ saveLocations(); >+ context.flushChanges(false); >+ } >+ >+ /** >+ * >+ * @return true if this algorithm is set to resize elements >+ */ >+ public boolean isResizing() { >+ return resize; >+ } >+ >+ /** >+ * >+ * @param resizing >+ * true if this algorithm should resize elements (default is >+ * false) >+ */ >+ public void setResizing(boolean resizing) { >+ resize = resizing; >+ } >+ >+ /** >+ * Sets the spring layout move-control. >+ * >+ * @param move >+ * The move-control value. >+ */ >+ public void setSpringMove(double move) { >+ sprMove = move; >+ } >+ >+ /** >+ * Returns the move-control value of this SpringLayoutAlgorithm in double >+ * presion. >+ * >+ * @return The move-control value. >+ */ >+ public double getSpringMove() { >+ return sprMove; >+ } >+ >+ /** >+ * Sets the spring layout strain-control. >+ * >+ * @param strain >+ * The strain-control value. >+ */ >+ public void setSpringStrain(double strain) { >+ sprStrain = strain; >+ } >+ >+ /** >+ * Returns the strain-control value of this SpringLayoutAlgorithm in double >+ * presion. >+ * >+ * @return The strain-control value. >+ */ >+ public double getSpringStrain() { >+ return sprStrain; >+ } >+ >+ /** >+ * Sets the spring layout length-control. >+ * >+ * @param length >+ * The length-control value. >+ */ >+ public void setSpringLength(double length) { >+ sprLength = length; >+ } >+ >+ /** >+ * Gets the max time this algorithm will run for >+ * >+ * @return >+ */ >+ public long getSpringTimeout() { >+ return maxTimeMS; >+ } >+ >+ /** >+ * Sets the spring timeout >+ * >+ * @param timeout >+ */ >+ public void setSpringTimeout(long timeout) { >+ maxTimeMS = timeout; >+ } >+ >+ /** >+ * Returns the length-control value of this SpringLayoutAlgorithm in double >+ * presion. >+ * >+ * @return The length-control value. >+ */ >+ public double getSpringLength() { >+ return sprLength; >+ } >+ >+ /** >+ * Sets the spring layout gravitation-control. >+ * >+ * @param gravitation >+ * The gravitation-control value. >+ */ >+ public void setSpringGravitation(double gravitation) { >+ sprGravitation = gravitation; >+ } >+ >+ /** >+ * Returns the gravitation-control value of this SpringLayoutAlgorithm in >+ * double presion. >+ * >+ * @return The gravitation-control value. >+ */ >+ public double getSpringGravitation() { >+ return sprGravitation; >+ } >+ >+ /** >+ * Sets the number of iterations to be used. >+ * >+ * @param gravitation >+ * The number of iterations. >+ */ >+ public void setIterations(int iterations) { >+ sprIterations = iterations; >+ } >+ >+ /** >+ * Returns the number of iterations to be used. >+ * >+ * @return The number of iterations. >+ */ >+ public int getIterations() { >+ return sprIterations; >+ } >+ >+ /** >+ * Sets whether or not this SpringLayoutAlgorithm will layout the nodes >+ * randomly before beginning iterations. >+ * >+ * @param random >+ * The random placement value. >+ */ >+ public void setRandom(boolean random) { >+ sprRandom = random; >+ } >+ >+ /** >+ * Returns whether or not this SpringLayoutAlgorithm will layout the nodes >+ * randomly before beginning iterations. >+ */ >+ public boolean getRandom() { >+ return sprRandom; >+ } >+ >+ private long startTime = 0; >+ >+ private int[] counter; >+ >+ private int[] counterX; >+ >+ private int[] counterY; >+ >+ private void initLayout() { >+ entities = context.getEntities(); >+ bounds = context.getBounds(); >+ loadLocations(); >+ >+ srcDestToSumOfWeights = new double[entities.length][entities.length]; >+ HashMap entityToPosition = new HashMap(); >+ for (int i = 0; i < entities.length; i++) { >+ entityToPosition.put(entities[i], new Integer(i)); >+ } >+ >+ ConnectionLayout[] connections = context.getConnections(); >+ for (int i = 0; i < connections.length; i++) { >+ ConnectionLayout connection = connections[i]; >+ Integer source = (Integer) entityToPosition.get(getEntity(connection.getSource())); >+ Integer target = (Integer) entityToPosition.get(getEntity(connection.getTarget())); >+ if (source == null || target == null) >+ continue; >+ double weight = connection.getWeight(); >+ weight = (weight <= 0 ? 0.1 : weight); >+ srcDestToSumOfWeights[source.intValue()][target.intValue()] += weight; >+ srcDestToSumOfWeights[target.intValue()][source.intValue()] += weight; >+ } >+ >+ if (sprRandom) >+ placeRandomly(); // put vertices in random places >+ >+ iteration = 1; >+ >+ startTime = System.currentTimeMillis(); >+ } >+ >+ private EntityLayout getEntity(NodeLayout node) { >+ if (!node.isPruned()) >+ return node; >+ SubgraphLayout subgraph = node.getSubgraph(); >+ if (subgraph.isGraphEntity()) >+ return subgraph; >+ return null; >+ } >+ >+ private void loadLocations() { >+ if (locationsX == null || locationsX.length != entities.length) { >+ int length = entities.length; >+ locationsX = new double[length]; >+ locationsY = new double[length]; >+ sizeW = new double[length]; >+ sizeH = new double[length]; >+ forcesX = new double[length]; >+ forcesY = new double[length]; >+ counterX = new int[length]; >+ counterY = new int[length]; >+ } >+ for (int i = 0; i < entities.length; i++) { >+ DisplayIndependentPoint location = entities[i].getLocation(); >+ locationsX[i] = location.x; >+ locationsY[i] = location.y; >+ DisplayIndependentDimension size = entities[i].getSize(); >+ sizeW[i] = size.width; >+ sizeH[i] = size.height; >+ } >+ } >+ >+ private void saveLocations() { >+ if (entities == null) >+ return; >+ for (int i = 0; i < entities.length; i++) { >+ entities[i].setLocation(locationsX[i], locationsY[i]); >+ } >+ } >+ >+ /** >+ * Scales the current iteration counter based on how long the algorithm has >+ * been running for. You can set the MaxTime in maxTimeMS! >+ */ >+ private void setSprIterationsBasedOnTime() { >+ if (maxTimeMS <= 0) >+ return; >+ >+ long currentTime = System.currentTimeMillis(); >+ double fractionComplete = (double) ((double) (currentTime - startTime) / ((double) maxTimeMS)); >+ int currentIteration = (int) (fractionComplete * sprIterations); >+ if (currentIteration > iteration) { >+ iteration = currentIteration; >+ } >+ >+ } >+ >+ protected boolean performAnotherNonContinuousIteration() { >+ setSprIterationsBasedOnTime(); >+ return (iteration <= sprIterations); >+ } >+ >+ protected int getCurrentLayoutStep() { >+ return iteration; >+ } >+ >+ protected int getTotalNumberOfLayoutSteps() { >+ return sprIterations; >+ } >+ >+ protected void computeOneIteration() { >+ computeForces(); >+ computePositions(); >+ DisplayIndependentRectangle currentBounds = getLayoutBounds(); >+ improveBoundScaleX(currentBounds); >+ improveBoundScaleY(currentBounds); >+ moveToCenter(currentBounds); >+ iteration++; >+ } >+ >+ /** >+ * Puts vertices in random places, all between (0,0) and (1,1). >+ */ >+ public void placeRandomly() { >+ // If only one node in the data repository, put it in the middle >+ if (locationsX.length == 1) { >+ // If only one node in the data repository, put it in the middle >+ locationsX[0] = bounds.x + 0.5 * bounds.width; >+ locationsY[0] = bounds.y + 0.5 * bounds.height; >+ } else { >+ locationsX[0] = bounds.x; >+ locationsY[0] = bounds.y; >+ locationsX[1] = bounds.x + bounds.width; >+ locationsY[1] = bounds.y + bounds.height; >+ for (int i = 2; i < locationsX.length; i++) { >+ locationsX[i] = bounds.x + Math.random() * bounds.width; >+ locationsY[i] = bounds.y + Math.random() * bounds.height; >+ } >+ } >+ } >+ >+ // ///////////////////////////////////////////////////////////////// >+ // /// Protected Methods ///// >+ // ///////////////////////////////////////////////////////////////// >+ >+ /** >+ * Computes the force for each node in this SpringLayoutAlgorithm. The >+ * computed force will be stored in the data repository >+ */ >+ protected void computeForces() { >+ >+ double forcesX[][] = new double[2][this.forcesX.length]; >+ double forcesY[][] = new double[2][this.forcesX.length]; >+ double locationsX[] = new double[this.forcesX.length]; >+ double locationsY[] = new double[this.forcesX.length]; >+ >+ // // initialize all forces to zero >+ for (int j = 0; j < 2; j++) { >+ for (int i = 0; i < this.forcesX.length; i++) { >+ forcesX[j][i] = 0; >+ forcesY[j][i] = 0; >+ locationsX[i] = this.locationsX[i]; >+ locationsY[i] = this.locationsY[i]; >+ } >+ } >+ // TODO: Again really really slow! >+ >+ for (int k = 0; k < 2; k++) { >+ for (int i = 0; i < this.locationsX.length; i++) { >+ >+ for (int j = i + 1; j < locationsX.length; j++) { >+ double dx = (locationsX[i] - locationsX[j]) / bounds.width / boundsScaleX; >+ double dy = (locationsY[i] - locationsY[j]) / bounds.height / boundsScaleY; >+ double distance_sq = dx * dx + dy * dy; >+ // make sure distance and distance squared not too small >+ distance_sq = Math.max(MIN_DISTANCE * MIN_DISTANCE, distance_sq); >+ double distance = Math.sqrt(distance_sq); >+ >+ // If there are relationships between srcObj and destObj >+ // then decrease force on srcObj (a pull) in direction of >+ // destObj >+ // If no relation between srcObj and destObj then increase >+ // force on srcObj (a push) from direction of destObj. >+ double sumOfWeights = srcDestToSumOfWeights[i][j]; >+ >+ double f; >+ if (sumOfWeights > 0) { >+ // nodes are pulled towards each other >+ f = -sprStrain * Math.log(distance / sprLength) * sumOfWeights; >+ } else { >+ // nodes are repelled from each other >+ f = sprGravitation / (distance_sq); >+ } >+ double dfx = f * dx / distance; >+ double dfy = f * dy / distance; >+ >+ forcesX[k][i] += dfx; >+ forcesY[k][i] += dfy; >+ >+ forcesX[k][j] -= dfx; >+ forcesY[k][j] -= dfy; >+ } >+ } >+ >+ for (int i = 0; i < entities.length; i++) { >+ if (entities[i].isMovable()) { >+ double deltaX = sprMove * forcesX[k][i]; >+ double deltaY = sprMove * forcesY[k][i]; >+ >+ // constrain movement, so that nodes don't shoot way off to >+ // the >+ // edge >+ double dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY); >+ double maxMovement = 0.2d * sprMove; >+ if (dist > maxMovement) { >+ deltaX *= maxMovement / dist; >+ deltaY *= maxMovement / dist; >+ } >+ >+ locationsX[i] += deltaX * bounds.width * boundsScaleX; >+ locationsY[i] += deltaY * bounds.height * boundsScaleY; >+ } >+ } >+ >+ } >+ // // initialize all forces to zero >+ for (int i = 0; i < this.entities.length; i++) { >+ if (forcesX[0][i] * forcesX[1][i] < 0) { >+ this.forcesX[i] = 0; >+ // } else if ( this.locationsX[i] < 0 ) { >+ // this.forcesX[i] = forcesX[1][i] / 10; >+ // } else if ( this.locationsX[i] > boundsScale * bounds.width) >+ // { >+ // this.forcesX[i] = forcesX[1][i] / 10; >+ } else { >+ this.forcesX[i] = forcesX[1][i]; >+ } >+ >+ if (forcesY[0][i] * forcesY[1][i] < 0) { >+ this.forcesY[i] = 0; >+ // } else if ( this.locationsY[i] < 0 ) { >+ // this.forcesY[i] = forcesY[1][i] / 10; >+ // } else if ( this.locationsY[i] > boundsScale * bounds.height) >+ // { >+ // this.forcesY[i] = forcesY[1][i] / 10; >+ } else { >+ this.forcesY[i] = forcesY[1][i]; >+ } >+ >+ } >+ >+ } >+ >+ /** >+ * Computes the position for each node in this SpringLayoutAlgorithm. The >+ * computed position will be stored in the data repository. position = >+ * position + sprMove * force >+ */ >+ protected void computePositions() { >+ for (int i = 0; i < entities.length; i++) { >+ if (entities[i].isMovable()) { >+ double deltaX = sprMove * forcesX[i]; >+ double deltaY = sprMove * forcesY[i]; >+ >+ // constrain movement, so that nodes don't shoot way off to the >+ // edge >+ double dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY); >+ double maxMovement = 0.2d * sprMove; >+ if (dist > maxMovement) { >+ deltaX *= maxMovement / dist; >+ deltaY *= maxMovement / dist; >+ } >+ >+ locationsX[i] += deltaX * bounds.width * boundsScaleX; >+ locationsY[i] += deltaY * bounds.height * boundsScaleY; >+ } >+ } >+ } >+ >+ private DisplayIndependentRectangle getLayoutBounds() { >+ double minX, maxX, minY, maxY; >+ minX = minY = Double.POSITIVE_INFINITY; >+ maxX = maxY = Double.NEGATIVE_INFINITY; >+ >+ for (int i = 0; i < locationsX.length; i++) { >+ maxX = Math.max(maxX, locationsX[i] + sizeW[i] / 2); >+ minX = Math.min(minX, locationsX[i] - sizeW[i] / 2); >+ maxY = Math.max(maxY, locationsY[i] + sizeH[i] / 2); >+ minY = Math.min(minY, locationsY[i] - sizeH[i] / 2); >+ } >+ return new DisplayIndependentRectangle(minX, minY, maxX - minX, maxY - minY); >+ } >+ >+ private void improveBoundScaleX(DisplayIndependentRectangle currentBounds) { >+ double boundaryProportionX = currentBounds.width / bounds.width; >+ // double boundaryProportion = Math.max(currentBounds.width / >+ // bounds.width, currentBounds.height / bounds.height); >+ >+ // if (boundaryProportionX < 0.1) >+ // boundsScaleX *= 2; >+ // else if (boundaryProportionX < 0.5) >+ // boundsScaleX *= 1.4; >+ // else if (boundaryProportionX < 0.8) >+ // boundsScaleX *= 1.1; >+ if (boundaryProportionX < 0.9) { >+ boundsScaleX *= 1.01; >+ >+ // >+ // else if (boundaryProportionX > 1.8) { >+ // if (boundsScaleX < 0.01) >+ // return; >+ // boundsScaleX /= 1.05; >+ } else if (boundaryProportionX > 1) { >+ if (boundsScaleX < 0.01) >+ return; >+ boundsScaleX /= 1.01; >+ } >+ } >+ >+ private void improveBoundScaleY(DisplayIndependentRectangle currentBounds) { >+ double boundaryProportionY = currentBounds.height / bounds.height; >+ // double boundaryProportion = Math.max(currentBounds.width / >+ // bounds.width, currentBounds.height / bounds.height); >+ >+ // if (boundaryProportionY < 0.1) >+ // boundsScaleY *= 2; >+ // else if (boundaryProportionY < 0.5) >+ // boundsScaleY *= 1.4; >+ // else if (boundaryProportionY < 0.8) >+ // boundsScaleY *= 1.1; >+ if (boundaryProportionY < 0.9) { >+ boundsScaleY *= 1.01; >+ >+ // else if (boundaryProportionY > 1.8) { >+ // if (boundsScaleY < 0.01) >+ // return; >+ // boundsScaleY /= 1.05; >+ } else if (boundaryProportionY > 1) { >+ if (boundsScaleY < 0.01) >+ return; >+ boundsScaleY /= 1.01; >+ } >+ } >+ >+ // private void improveBoundsScale(DisplayIndependentRectangle >+ // currentBounds) { >+ // double boundaryProportionX = currentBounds.width / bounds.width; >+ // double boundaryProportionY = currentBounds.height / bounds.height; >+ // // double boundaryProportion = Math.max(currentBounds.width / >+ // // bounds.width, currentBounds.height / bounds.height); >+ // >+ // if (boundaryProportion < 0.1) >+ // boundsScale *= 2; >+ // else if (boundaryProportion < 0.5) >+ // boundsScale *= 1.4; >+ // else if (boundaryProportion < 0.8) >+ // boundsScale *= 1.1; >+ // else if (boundaryProportion < 0.99) >+ // boundsScale *= 1.05; >+ // >+ // else if (boundaryProportion > 1.8) { >+ // if (boundsScale < 0.01) >+ // return; >+ // boundsScale /= 1.05; >+ // } >+ // else if (boundaryProportion > 1) { >+ // if (boundsScale < 0.01) >+ // return; >+ // boundsScale /= 1.01; >+ // } >+ // >+ // } >+ >+ private void moveToCenter(DisplayIndependentRectangle currentBounds) { >+ double moveX = (currentBounds.x + currentBounds.width / 2) - (bounds.x + bounds.width / 2); >+ double moveY = (currentBounds.y + currentBounds.height / 2) - (bounds.y + bounds.height / 2); >+ for (int i = 0; i < locationsX.length; i++) { >+ locationsX[i] -= moveX; >+ locationsY[i] -= moveY; >+ } >+ } >+} >Index: src/org/eclipse/zest/layouts/algorithms/TreeLayoutAlgorithm.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.layouts/src/org/eclipse/zest/layouts/algorithms/TreeLayoutAlgorithm.java,v >retrieving revision 1.7 >diff -u -r1.7 TreeLayoutAlgorithm.java >--- src/org/eclipse/zest/layouts/algorithms/TreeLayoutAlgorithm.java 12 Sep 2007 20:44:37 -0000 1.7 >+++ src/org/eclipse/zest/layouts/algorithms/TreeLayoutAlgorithm.java 15 Mar 2010 03:49:08 -0000 >@@ -1,580 +1,178 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts.algorithms; >- >-import java.util.ArrayList; >-import java.util.Arrays; >-import java.util.Collection; >-import java.util.Collections; >-import java.util.Comparator; >-import java.util.HashSet; >-import java.util.Iterator; >-import java.util.List; >-import java.util.Set; >- >-import org.eclipse.zest.layouts.LayoutStyles; >-import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle; >-import org.eclipse.zest.layouts.dataStructures.InternalNode; >-import org.eclipse.zest.layouts.dataStructures.InternalRelationship; >-import org.eclipse.zest.layouts.exampleStructures.SimpleRelationship; >- >- >- >-/** >- * The TreeLayoutAlgorithm class implements a simple algorithm to >- * arrange graph nodes in a layered vertical tree-like layout. >- * >- * This is by no means an efficiently coded algorithm. >- * >- * @version 2.0 >- * @author Casey Best and Rob Lintern (version 1.0 by Jingwei Wu) >- */ >-public class TreeLayoutAlgorithm extends AbstractLayoutAlgorithm { >- >- private final static double DEFAULT_WEIGHT = 0; >- private final static boolean DEFAULT_MARKED = false; >- >- private final static boolean AS_DESTINATION = false; >- private final static boolean AS_SOURCE = true; >- >- private final static int NUM_DESCENDENTS_INDEX = 0; >- private final static int NUM_LEVELS_INDEX = 1; >- >- private ArrayList treeRoots; >- >- private double boundsX; >- private double boundsY; >- private double boundsWidth; >- private double boundsHeight; >- private DisplayIndependentRectangle layoutBounds = null; >- >- private List [] parentLists; >- private List [] childrenLists; >- private double [] weights; >- private boolean [] markedArr; >- >- ///////////////////////////////////////////////////////////////////////// >- ///// Constructors ///// >- ///////////////////////////////////////////////////////////////////////// >- >- /** >- * Constructs a new TreeLayoutAlgorithm object. >- */ >- public TreeLayoutAlgorithm( int styles ) { >- super( styles ); >- } >- >- /** >- * Tree layout algorithm Constructor with NO Style >- * >- */ >- public TreeLayoutAlgorithm() { >- this( LayoutStyles.NONE ); >- } >- >- ///////////////////////////////////////////////////////////////////////// >- ///// Public Methods ///// >- ///////////////////////////////////////////////////////////////////////// >- >- public void setLayoutArea(double x, double y, double width, double height) { >- throw new RuntimeException(); >- } >- >- protected int getCurrentLayoutStep() { >- // TODO Auto-generated method stub >- return 0; >- } >- >- protected int getTotalNumberOfLayoutSteps() { >- return 4; >- } >- >- /** >- * Executes this TreeLayoutAlgorithm layout algorithm by referencing the >- * data stored in the repository system. Once done, the result >- * will be saved to the data repository. >- * >- * @param entitiesToLayout Apply the algorithm to these entities >- * @param relationshipsToConsider Only consider these relationships when applying the algorithm. >- * @param boundsX The left side of the bounds in which the layout can place the entities. >- * @param boundsY The top side of the bounds in which the layout can place the entities. >- * @param boundsWidth The width of the bounds in which the layout can place the entities. >- * @param boundsHeight The height of the bounds in which the layout can place the entities. >- * @throws RuntimeException Thrown if entitiesToLayout doesn't contain all of the endpoints for each relationship in relationshipsToConsider >- */ >- protected void preLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height) { >- // Filter unwanted entities and relationships >- //super.applyLayout (entitiesToLayout, relationshipsToConsider, boundsX, boundsY, boundsWidth, boundsHeight); >- >- parentLists = new List [entitiesToLayout.length]; >- childrenLists = new List [entitiesToLayout.length]; >- weights = new double [entitiesToLayout.length]; >- markedArr = new boolean [entitiesToLayout.length]; >- for (int i = 0; i < entitiesToLayout.length; i++) { >- parentLists[i] = new ArrayList(); >- childrenLists[i] = new ArrayList(); >- weights[i] = DEFAULT_WEIGHT; >- markedArr[i] = DEFAULT_MARKED; >- } >- >- this.boundsHeight = height; >- this.boundsWidth = width; >- this.boundsX = x; >- this.boundsY = y; >- layoutBounds = new DisplayIndependentRectangle(boundsX, boundsY, boundsWidth, boundsHeight); >- >- } >- >- protected void applyLayoutInternal(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double boundsX, double boundsY, double boundsWidth, double boundsHeight) { >- >- if (entitiesToLayout.length > 0) { >- int totalProgress = 4; >- fireProgressEvent (1, totalProgress); >- >- //List roots = new ArrayList(); >- treeRoots = new ArrayList(); >- buildForest(treeRoots, entitiesToLayout, relationshipsToConsider); >- fireProgressEvent (2, totalProgress); >- computePositions(treeRoots, entitiesToLayout); >- fireProgressEvent (3, totalProgress); >- defaultFitWithinBounds(entitiesToLayout, layoutBounds); >- >- } >- } >- >- protected void postLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider) { >- updateLayoutLocations(entitiesToLayout); >- fireProgressEvent (4, 4); >- } >- >- /** >- * Returns the last found roots >- */ >- public List getRoots () { >- return treeRoots; >- } >- >- /** >- * Finds all the relationships in which the node <code>obj<code> >- * plays the specified <code>role</code>. >- * @param entity The node that concerns the relations to be found. >- * @param role The role played by the <code>obj</code>. Its type >- * must be of <code>ACTOR_ROLE</code> or <code>ACTEE_ROLE</code>. >- * @see SimpleRelationship >- */ >- private Collection findRelationships(Object entity, boolean objectAsSource, InternalRelationship [] relationshipsToConsider) { >- Collection foundRels = new ArrayList(); >- for (int i = 0; i < relationshipsToConsider.length; i++) { >- InternalRelationship rel = relationshipsToConsider[i]; >- if (objectAsSource && rel.getSource().equals (entity)) { >- foundRels.add(rel); >- } else if (!objectAsSource && rel.getDestination().equals (entity)) { >- foundRels.add(rel); >- } >- } >- return foundRels; >- } >- >- /** >- * Finds the relation that has the lowest index in the relation >- * repository in which the node <code>obj<code> plays the specified >- * <code>role</code>. >- * @param obj The node that concerns the relations to be found. >- * @param role The role played by the <code>obj</code>. Its type must >- * be of <code>ACTOR_ROLE</code> or <code>ACTEE_ROLE</code>. >- * @see SimpleRelationship >- * @see SimpleRelationship#ACTOR_ROLE >- * @see SimpleRelationship#ACTEE_ROLE >- */ >- private InternalRelationship findRelationship(Object entity, boolean objectAsSource, InternalRelationship [] relationshipsToConsider) { >- InternalRelationship relationship = null; >- for (int i = 0; i < relationshipsToConsider.length && relationship == null; i++) { >- InternalRelationship possibleRel = relationshipsToConsider[i]; >- if (objectAsSource && possibleRel.getSource().equals (entity)) { >- relationship = possibleRel; >- } else if (!objectAsSource && possibleRel.getDestination().equals (entity)) { >- relationship = possibleRel; >- } >- } >- return relationship; >- } >- >- >- >- ///////////////////////////////////////////////////////////////////////// >- ///// Private Methods ///// >- ///////////////////////////////////////////////////////////////////////// >- >- >- /** >- * Builds the tree forest that is used to calculate positions >- * for each node in this TreeLayoutAlgorithm. >- */ >- private void buildForest(List roots, InternalNode [] entities, InternalRelationship [] relationships) { >- List unplacedEntities = new ArrayList (Arrays.asList(entities)); >- buildForestRecursively(roots, unplacedEntities, entities, relationships); >- } >- >- /** >- * Builds the forest recursively. All entities >- * will be placed somewhere in the forest. >- */ >- private void buildForestRecursively(List roots, List unplacedEntities, InternalNode [] entities, InternalRelationship [] relationships) { >- if (unplacedEntities.size() == 0) { >- return; // no more entities to place >- } >- >- // get the first entity in the list of unplaced entities, find its root, and build this root's tree >- InternalNode layoutEntity = (InternalNode) unplacedEntities.get(0); >- InternalNode rootEntity = findRootObjectRecursive(layoutEntity, new HashSet(), relationships); >- int rootEntityIndex = indexOfInternalNode(entities, rootEntity); >- buildTreeRecursively(rootEntity, rootEntityIndex, 0, entities, relationships); >- roots.add(rootEntity); >- >- // now see which nodes are left to be placed in a tree somewhere >- List unmarkedCopy = new ArrayList(unplacedEntities); >- for (Iterator iter = unmarkedCopy.iterator(); iter.hasNext();) { >- InternalNode tmpEntity = (InternalNode) iter.next(); >- int tmpEntityIndex = indexOfInternalNode(entities, tmpEntity); >- boolean isMarked = markedArr[tmpEntityIndex]; >- if (isMarked) { >- unplacedEntities.remove(tmpEntity); >- } >- } >- buildForestRecursively(roots, unplacedEntities, entities, relationships); >- } >- >- /** >- * Finds the root node that can be treated as the root of a tree. >- * The found root node should be one of the unmarked nodes. >- */ >- private InternalNode findRootObjectRecursive(InternalNode currentEntity, Set seenAlready, InternalRelationship [] relationshipsToConsider) { >- InternalNode rootEntity = null; >- InternalRelationship rel = findRelationship(currentEntity, AS_DESTINATION, relationshipsToConsider); >- if (rel == null) { >- rootEntity = currentEntity; >- } else { >- InternalNode parentEntity = rel.getSource(); >- if (!seenAlready.contains(parentEntity)) { >- seenAlready.add(parentEntity); >- rootEntity = findRootObjectRecursive(parentEntity, seenAlready, relationshipsToConsider); >- } else { >- rootEntity = currentEntity; >- } >- } >- return rootEntity; >- } >- >- >- >- /** >- * Builds a tree of the passed in entity. >- * The entity will pass a weight value to all of its children recursively. >- */ >- private void buildTreeRecursively(InternalNode layoutEntity, int i, double weight, InternalNode [] entities, final InternalRelationship [] relationships) { >- // No need to do further computation! >- if (layoutEntity == null) { >- return; >- } >- >- // A marked entity means that it has been added to the >- // forest, and its weight value needs to be modified. >- if (markedArr[i]) { >- modifyWeightRecursively(layoutEntity, i, weight, new HashSet(), entities, relationships); >- return; //No need to do further computation. >- } >- >- // Mark this entity, set its weight value and create a new tree node. >- markedArr[i] = true; >- weights[i] = weight; >- >- // collect the children of this entity and put them in order >- Collection rels = findRelationships(layoutEntity, AS_SOURCE, relationships); >- List children = new ArrayList (); >- for (Iterator iter = rels.iterator(); iter.hasNext();) { >- InternalRelationship layoutRel = (InternalRelationship) iter.next(); >- InternalNode childEntity = layoutRel.getDestination(); >- children.add(childEntity); >- } >- >- if (comparator != null) { >- Collections.sort(children, comparator); >- } else { >- // sort the children by level, then by number of descendents, then by number of children >- // TODO: SLOW >- Collections.sort(children, new Comparator () { >- public int compare(Object o1, Object o2) { >- InternalNode node1 = (InternalNode) o1; >- InternalNode node2 = (InternalNode) o2; >- int [] numDescendentsAndLevel1 = new int [2]; >- int [] numDescendentsAndLevel2 = new int [2]; >- int level1 = numDescendentsAndLevel1[NUM_LEVELS_INDEX]; >- int level2 = numDescendentsAndLevel2[NUM_LEVELS_INDEX]; >- if (level1 == level2) { >- getNumDescendentsAndLevel(node1, relationships, numDescendentsAndLevel1); >- getNumDescendentsAndLevel(node2, relationships, numDescendentsAndLevel2); >- int numDescendents1 = numDescendentsAndLevel1[NUM_DESCENDENTS_INDEX]; >- int numDescendents2 = numDescendentsAndLevel2[NUM_DESCENDENTS_INDEX]; >- if (numDescendents1 == numDescendents2) { >- int numChildren1 = getNumChildren(node1, relationships); >- int numChildren2 = getNumChildren(node1, relationships); >- return numChildren2 - numChildren1; >- } else { >- return numDescendents2 - numDescendents1; >- } >- } else { >- return level2 - level1; >- } >- //return getNumChildren(node2, relationships) - getNumChildren(node1, relationships); >- } >- }); >- } >- >- // map children to this parent, and vice versa >- for (Iterator iter = children.iterator(); iter.hasNext();) { >- InternalNode childEntity = (InternalNode) iter.next(); >- int childEntityIndex = indexOfInternalNode(entities, childEntity); >- if (!childrenLists[i].contains(childEntity)) { >- childrenLists[i].add(childEntity); >- } >- if (!parentLists[childEntityIndex].contains(layoutEntity)) { >- parentLists[childEntityIndex].add(layoutEntity); >- } >- } >- >- for (Iterator iter = children.iterator(); iter.hasNext();) { >- InternalNode childEntity = (InternalNode) iter.next(); >- int childEntityIndex = indexOfInternalNode(entities, childEntity); >- buildTreeRecursively(childEntity, childEntityIndex, weight + 1, entities, relationships); >- } >- } >- >- private int getNumChildren (InternalNode layoutEntity, InternalRelationship [] relationships) { >- return findRelationships(layoutEntity, AS_SOURCE, relationships).size(); >- } >- >- private void getNumDescendentsAndLevel (InternalNode layoutEntity, InternalRelationship [] relationships, int [] numDescendentsAndLevel) { >- getNumDescendentsAndLevelRecursive(layoutEntity, relationships, new HashSet(), numDescendentsAndLevel, 0); >- } >- >- private void getNumDescendentsAndLevelRecursive (InternalNode layoutEntity, InternalRelationship [] relationships, Set seenAlready, int [] numDescendentsAndLevel, int currentLevel) { >- if (seenAlready.contains(layoutEntity)) { >- return; >- } >- seenAlready.add(layoutEntity); >- numDescendentsAndLevel[NUM_LEVELS_INDEX] = Math.max(numDescendentsAndLevel[NUM_LEVELS_INDEX], currentLevel); >- Collection rels = findRelationships(layoutEntity, AS_SOURCE, relationships); >- for (Iterator iter = rels.iterator(); iter.hasNext();) { >- InternalRelationship layoutRel = (InternalRelationship) iter.next(); >- InternalNode childEntity = layoutRel.getDestination(); >- numDescendentsAndLevel[NUM_DESCENDENTS_INDEX]++; >- getNumDescendentsAndLevelRecursive(childEntity, relationships, seenAlready, numDescendentsAndLevel, currentLevel + 1); >- >- } >- } >- >- >- /** >- * Modifies the weight value of the marked node recursively. >- */ >- private void modifyWeightRecursively(InternalNode layoutEntity, int i, double weight, Set descendentsSeenSoFar, InternalNode [] entities, InternalRelationship [] relationships) { >- // No need to do further computation! >- if (layoutEntity == null) { >- return; >- } >- >- if (descendentsSeenSoFar.contains(layoutEntity)) { >- return; //No need to do further computation. >- } >- >- descendentsSeenSoFar.add(layoutEntity); >- // No need to do further computation! >- if (weight < weights[i]) { >- return; >- } >- >- weights[i] = weight; >- Collection rels = findRelationships(layoutEntity, AS_SOURCE, relationships); >- >- >- for (Iterator iter = rels.iterator(); iter.hasNext();) { >- InternalRelationship tmpRel = (InternalRelationship) iter.next(); >- InternalNode tmpEntity = tmpRel.getDestination(); >- int tmpEntityIndex = indexOfInternalNode(entities, tmpEntity); >- modifyWeightRecursively(tmpEntity, tmpEntityIndex, weight + 1, descendentsSeenSoFar, entities, relationships); >- } >- } >- >- /** >- * Gets the maxium weight of a tree in the forest of this TreeLayoutAlgorithm. >- */ >- private double getMaxiumWeightRecursive(InternalNode layoutEntity, int i, Set seenAlready, InternalNode [] entities) { >- double result = 0; >- if (seenAlready.contains(layoutEntity)) { >- return result; >- } >- seenAlready.add(layoutEntity); >- List children = childrenLists[i]; >- if (children.isEmpty()) { >- result = weights[i]; >- } else { >- //TODO: SLOW >- for (Iterator iter = children.iterator(); iter.hasNext();) { >- InternalNode childEntity = (InternalNode) iter.next(); >- int childEntityIndex = indexOfInternalNode(entities, childEntity); >- result = Math.max(result, getMaxiumWeightRecursive(childEntity, childEntityIndex, seenAlready, entities)); >- } >- } >- return result; >- } >- >- /** >- * Computes positions for each node in this TreeLayoutAlgorithm by >- * referencing the forest that holds those nodes. >- */ >- private void computePositions(List roots, InternalNode [] entities) { >- // No need to do further computation! >- if (roots.size() == 0) { >- return; >- } >- >- int totalLeafCount = 0; >- double maxWeight = 0; >- for (int i = 0; i < roots.size(); i++) { >- InternalNode rootEntity = (InternalNode) roots.get(i); >- int rootEntityIndex = indexOfInternalNode(entities, rootEntity); >- totalLeafCount = totalLeafCount + getNumberOfLeaves(rootEntity, rootEntityIndex, entities); >- maxWeight = Math.max(maxWeight, getMaxiumWeightRecursive(rootEntity, rootEntityIndex, new HashSet(), entities) + 1.0); >- } >- >- double width = 1.0 / totalLeafCount; >- double height = 1.0 / maxWeight; >- >- int leafCountSoFar = 0; >- >- //TODO: SLOW! >- for (int i = 0; i < roots.size(); i++) { >- InternalNode rootEntity = (InternalNode) roots.get(i); >- int rootEntityIndex = indexOfInternalNode(entities, rootEntity); >- computePositionRecursively(rootEntity, rootEntityIndex, leafCountSoFar, width, height, new HashSet(), entities); >- leafCountSoFar = leafCountSoFar + getNumberOfLeaves(rootEntity, rootEntityIndex, entities); >- } >- } >- >- /** >- * Computes positions recursively until the leaf nodes are reached. >- */ >- private void computePositionRecursively(InternalNode layoutEntity, int i, int relativePosition, double width, double height, Set seenAlready, InternalNode [] entities) { >- if (seenAlready.contains(layoutEntity)) { >- return; >- } >- seenAlready.add(layoutEntity); >- double level = getLevel(layoutEntity, i, entities); >- int breadth = getNumberOfLeaves(layoutEntity, i, entities); >- double absHPosition = relativePosition + breadth / 2.0; >- double absVPosition = (level + 0.5); >- >- double posx = absHPosition * width; >- double posy = absVPosition * height; >- double weight = weights[i]; >- posy = posy + height * (weight - level); >- layoutEntity.setInternalLocation( posx, posy ); >- >- >- int relativeCount = 0; >- List children = childrenLists[i]; >- //TODO: Slow >- for (Iterator iter = children.iterator(); iter.hasNext();) { >- InternalNode childEntity = (InternalNode) iter.next(); >- int childEntityIndex = indexOfInternalNode(entities, childEntity); >- computePositionRecursively(childEntity, childEntityIndex, relativePosition + relativeCount, width, height, seenAlready, entities); >- relativeCount = relativeCount + getNumberOfLeaves(childEntity, childEntityIndex, entities); >- } >- } >- >- private int getNumberOfLeaves (InternalNode layoutEntity, int i, InternalNode [] entities) { >- return getNumberOfLeavesRecursive(layoutEntity, i, new HashSet(), entities); >- } >- >- private int getNumberOfLeavesRecursive(InternalNode layoutEntity, int i, Set seen, InternalNode [] entities) { >- int numLeaves = 0; >- List children = childrenLists[i]; >- if (children.size() == 0) { >- numLeaves = 1; >- } else { >- //TODO: SLOW! >- for (Iterator iter = children.iterator(); iter.hasNext();) { >- InternalNode childEntity = (InternalNode) iter.next(); >- if (!seen.contains(childEntity)) { >- seen.add (childEntity); >- int childEntityIndex = indexOfInternalNode(entities, childEntity); >- numLeaves += getNumberOfLeavesRecursive(childEntity, childEntityIndex, seen, entities); >- } else { >- numLeaves = 1; >- } >- } >- } >- return numLeaves; >- } >- >- private int getLevel (InternalNode layoutEntity, int i, InternalNode [] entities) { >- return getLevelRecursive(layoutEntity, i, new HashSet(), entities); >- } >- >- private int getLevelRecursive(InternalNode layoutEntity, int i, Set seen, InternalNode [] entities) { >- if (seen.contains(layoutEntity)) { >- return 0; >- } >- seen.add(layoutEntity); >- List parents = parentLists[i]; >- int maxParentLevel = 0; >- for (Iterator iter = parents.iterator(); iter.hasNext();) { >- InternalNode parentEntity = (InternalNode) iter.next(); >- int parentEntityIndex = indexOfInternalNode(entities, parentEntity); >- int parentLevel = getLevelRecursive(parentEntity, parentEntityIndex, seen, entities) + 1; >- maxParentLevel = Math.max(maxParentLevel, parentLevel); >- } >- return maxParentLevel; >- } >- >- /** >- * Note: Use this as little as possible! >- * TODO limit the use of this method >- * @param nodes >- * @param nodeToFind >- * @return >- */ >- private int indexOfInternalNode (InternalNode [] nodes, InternalNode nodeToFind) { >- for (int i = 0; i < nodes.length; i++) { >- InternalNode node = nodes[i]; >- if (node.equals(nodeToFind)) { >- return i; >- } >- } >- throw new RuntimeException("Couldn't find index of internal node: " + nodeToFind); >- } >- >- >- protected boolean isValidConfiguration(boolean asynchronous, boolean continueous) { >- if ( asynchronous && continueous ) { >- return false; >- } else if ( asynchronous && !continueous ) { >- return true; >- } else if ( !asynchronous && continueous ) { >- return false; >- } else if ( !asynchronous && !continueous ) { >- return true; >- } >- >- return false; >- } >- >-} >+/******************************************************************************* >+ * Copyright (c) 2005-2010 The Chisel Group and others. 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 - initial API and implementation >+ * Mateusz Matela >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.layouts.algorithms; >+ >+import java.util.Iterator; >+ >+import org.eclipse.zest.layouts.LayoutAlgorithm; >+import org.eclipse.zest.layouts.algorithms.TreeLayoutObserver.TreeNode; >+import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle; >+import org.eclipse.zest.layouts.interfaces.EntityLayout; >+import org.eclipse.zest.layouts.interfaces.LayoutContext; >+ >+/** >+ * The TreeLayoutAlgorithm class implements a simple algorithm to arrange graph >+ * nodes in a layered tree-like layout. >+ * >+ * @version 3.0 >+ * @author Mateusz Matela >+ * @author Casey Best and Rob Lintern (version 2.0) >+ * @author Jingwei Wu (version 1.0) >+ */ >+public class TreeLayoutAlgorithm implements LayoutAlgorithm { >+ >+ /** >+ * Tree direction constant for which root is placed at the top and branches >+ * spread downwards >+ */ >+ public final static int TOP_DOWN = 1; >+ >+ /** >+ * Tree direction constant for which root is placed at the bottom and >+ * branches spread upwards >+ */ >+ public final static int BOTTOM_UP = 2; >+ >+ /** >+ * Tree direction constant for which root is placed at the left and branches >+ * spread to the right >+ */ >+ public final static int LEFT_RIGHT = 3; >+ >+ /** >+ * Tree direction constant for which root is placed at the right and >+ * branches spread to the left >+ */ >+ public final static int RIGHT_LEFT = 4; >+ >+ private int direction = TOP_DOWN; >+ >+ private boolean resize = false; >+ >+ private LayoutContext context; >+ >+ private DisplayIndependentRectangle bounds; >+ >+ private double leafSize, layerSize; >+ >+ private TreeLayoutObserver treeObserver; >+ >+ public TreeLayoutAlgorithm() { >+ } >+ >+ public TreeLayoutAlgorithm(int direction) { >+ setDirection(direction); >+ } >+ >+ public int getDirection() { >+ return direction; >+ } >+ >+ public void setDirection(int direction) { >+ if (direction == TOP_DOWN || direction == BOTTOM_UP || direction == LEFT_RIGHT || direction == RIGHT_LEFT) >+ this.direction = direction; >+ else >+ throw new IllegalArgumentException("Invalid direction: " + direction); >+ } >+ >+ /** >+ * >+ * @return true if this algorithm is set to resize elements >+ */ >+ public boolean isResizing() { >+ return resize; >+ } >+ >+ /** >+ * >+ * @param resizing >+ * true if this algorithm should resize elements (default is >+ * false) >+ */ >+ public void setResizing(boolean resizing) { >+ resize = resizing; >+ } >+ >+ public void setLayoutContext(LayoutContext context) { >+ if (treeObserver != null) { >+ treeObserver.stop(); >+ } >+ this.context = context; >+ treeObserver = new TreeLayoutObserver(context, null); >+ } >+ >+ public void applyLayout(boolean clean) { >+ if (!clean) >+ return; >+ >+ internalApplyLayout(); >+ >+ EntityLayout[] entities = context.getEntities(); >+ >+ if (resize) >+ AlgorithmHelper.maximizeSizes(entities); >+ >+ DisplayIndependentRectangle bounds2 = new DisplayIndependentRectangle(bounds); >+ int insets = 4; >+ bounds2.x += insets; >+ bounds2.y += insets; >+ bounds2.width -= 2 * insets; >+ bounds2.height -= 2 * insets; >+ AlgorithmHelper.fitWithinBounds(entities, bounds2, resize); >+ } >+ >+ void internalApplyLayout() { >+ TreeNode superRoot = treeObserver.getSuperRoot(); >+ bounds = context.getBounds(); >+ if (direction == TOP_DOWN || direction == BOTTOM_UP) { >+ leafSize = bounds.width / superRoot.numOfLeaves; >+ layerSize = bounds.height / superRoot.height; >+ } else { >+ leafSize = bounds.height / superRoot.numOfLeaves; >+ layerSize = bounds.width / superRoot.height; >+ } >+ int leafCountSoFar = 0; >+ for (Iterator iterator = superRoot.getChildren().iterator(); iterator.hasNext();) { >+ TreeNode rootInfo = (TreeNode) iterator.next(); >+ computePositionRecursively(rootInfo, leafCountSoFar); >+ leafCountSoFar = leafCountSoFar + rootInfo.numOfLeaves; >+ } >+ } >+ >+ /** >+ * Computes positions recursively until the leaf nodes are reached. >+ */ >+ private void computePositionRecursively(TreeNode entityInfo, int relativePosition) { >+ double breadthPosition = relativePosition + entityInfo.numOfLeaves / 2.0; >+ double depthPosition = (entityInfo.depth + 0.5); >+ >+ switch (direction) { >+ case TOP_DOWN: >+ entityInfo.getNode().setLocation(breadthPosition * leafSize, depthPosition * layerSize); >+ break; >+ case BOTTOM_UP: >+ entityInfo.getNode().setLocation(breadthPosition * leafSize, bounds.height - depthPosition * layerSize); >+ break; >+ case LEFT_RIGHT: >+ entityInfo.getNode().setLocation(depthPosition * layerSize, breadthPosition * leafSize); >+ break; >+ case RIGHT_LEFT: >+ entityInfo.getNode().setLocation(bounds.width - depthPosition * layerSize, breadthPosition * leafSize); >+ break; >+ } >+ >+ for (Iterator iterator = entityInfo.children.iterator(); iterator.hasNext();) { >+ TreeNode childInfo = (TreeNode) iterator.next(); >+ computePositionRecursively(childInfo, relativePosition); >+ relativePosition += childInfo.numOfLeaves; >+ } >+ } >+} >Index: src/org/eclipse/zest/layouts/algorithms/TreeLayoutObserver.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/algorithms/TreeLayoutObserver.java >diff -N src/org/eclipse/zest/layouts/algorithms/TreeLayoutObserver.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/zest/layouts/algorithms/TreeLayoutObserver.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,552 @@ >+/******************************************************************************* >+ * Copyright (c) 2005-2010 The Chisel Group and others. 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 - initial API and implementation >+ * Mateusz Matela >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.layouts.algorithms; >+ >+import java.util.ArrayList; >+import java.util.Collections; >+import java.util.HashMap; >+import java.util.HashSet; >+import java.util.Iterator; >+import java.util.LinkedList; >+import java.util.List; >+import java.util.ListIterator; >+import java.util.Set; >+ >+import org.eclipse.zest.layouts.interfaces.ConnectionLayout; >+import org.eclipse.zest.layouts.interfaces.GraphStructureListener; >+import org.eclipse.zest.layouts.interfaces.LayoutContext; >+import org.eclipse.zest.layouts.interfaces.NodeLayout; >+ >+/** >+ * A helper class for layout algorithms that are based on tree structure. It >+ * keeps track of changes in observed layout context and stores current >+ * information about the tree structure - children of each node and several >+ * other parameters. >+ */ >+public class TreeLayoutObserver { >+ >+ /** >+ * <code>TreeLayoutObserver</code> uses instance of this class to create >+ * instances of {@link TreeNode}. It may be extended and passed to >+ * <code>TreeLayoutObserver</code>'s constructor in order to build a tree >+ * structure made of <code>TreeNode</code>'s subclasses. >+ */ >+ public static class TreeNodeFactory { >+ public TreeNode createTreeNode(NodeLayout nodeLayout, TreeLayoutObserver observer) { >+ return new TreeNode(nodeLayout, observer); >+ } >+ } >+ >+ /** >+ * Represents a node in a tree structure and stores all information related >+ * to it. May be subclassed if additional data and behavior is necessary. >+ */ >+ public static class TreeNode { >+ final protected NodeLayout node; >+ final protected TreeLayoutObserver owner; >+ protected int height = 0; >+ protected int depth = -1; >+ protected int numOfLeaves = 0; >+ protected int numOfDescendants = 0; >+ protected int order = 0; >+ protected final List children = new ArrayList(); >+ protected TreeNode parent; >+ protected boolean firstChild = false, lastChild = false; >+ >+ /** >+ * >+ * @return node layout related to this tree node (null for >+ * {@link TreeLayoutObserver#getSuperRoot() Super Root}) >+ */ >+ public NodeLayout getNode() { >+ return node; >+ } >+ >+ /** >+ * >+ * @return <code>TreeLayoutObserver</code> owning this tree node >+ */ >+ public TreeLayoutObserver getOwner() { >+ return owner; >+ } >+ >+ /** >+ * >+ * @return height of this node in the tree (the longest distance to a >+ * leaf, 0 for a leaf itself) >+ */ >+ public int getHeight() { >+ return height; >+ } >+ >+ /** >+ * >+ * @return depth of this node in the tree (distance from root, 0 for a >+ * root and -1 for {@link TreeLayoutObserver#getSuperRoot() >+ * Super Root} >+ */ >+ public int getDepth() { >+ return depth; >+ } >+ >+ /** >+ * >+ * @return number of all leaves descending from this node (1 for a leaf >+ * itself) >+ */ >+ public int getNumOfLeaves() { >+ return numOfLeaves; >+ } >+ >+ /** >+ * >+ * @return total number of descendants of this node (0 for leafs) >+ */ >+ public int getNumOfDescendants() { >+ return numOfDescendants; >+ } >+ >+ /** >+ * Returns order in which nodes are visited during Deep First Search. >+ * Children are visited in the same order as they were added to their >+ * layout context, unless {@link TreeLayoutObserver#recomputeTree()} was >+ * called after the nodes were added. In that case the order is >+ * determined by order of nodes returned by >+ * {@link NodeLayout#getSuccessingNodes()}. Leaves are assigned >+ * successive numbers starting from 0, other nodes have order equal to >+ * the smallest order of their children. >+ * >+ * @return order of this node >+ */ >+ public int getOrder() { >+ return order; >+ } >+ >+ /** >+ * >+ * @return an unmodifiable list of this node's children >+ */ >+ public List getChildren() { >+ return Collections.unmodifiableList(children); >+ } >+ >+ /** >+ * >+ * @return this node's parent >+ */ >+ public TreeNode getParent() { >+ return parent; >+ } >+ >+ /** >+ * >+ * @return true if this node is the first child of its parent (has the >+ * smallest order) >+ */ >+ public boolean isFirstChild() { >+ return firstChild; >+ } >+ >+ /** >+ * >+ * @return >+ */ >+ public boolean isLastChild() { >+ return lastChild; >+ } >+ >+ /** >+ * Creates a tree node related to given layout node >+ * >+ * @param node >+ * the layout node >+ * @param owner >+ * <code>TreeLayoutObserver</code> owning created node >+ */ >+ protected TreeNode(NodeLayout node, TreeLayoutObserver owner) { >+ this.node = node; >+ this.owner = owner; >+ } >+ >+ /** >+ * Adds given node to the list of this node's children and set its >+ * parent to this node. >+ * >+ * @param child >+ * node to add >+ */ >+ protected void addChild(TreeNode child) { >+ children.add(child); >+ child.parent = this; >+ } >+ >+ /** >+ * Performs a DFS on the tree structure and calculates all parameters of >+ * its nodes. Should be called on >+ * {@link TreeLayoutObserver#getSuperRoot() Super Root}. Uses recurrence >+ * to go through all the nodes. >+ */ >+ protected void precomputeTree() { >+ if (children.isEmpty()) { >+ height = 0; >+ numOfLeaves = 1; >+ numOfDescendants = 0; >+ } else { >+ height = 0; >+ numOfLeaves = 0; >+ numOfDescendants = 0; >+ for (ListIterator iterator = children.listIterator(); iterator.hasNext();) { >+ TreeNode child = (TreeNode) iterator.next(); >+ child.depth = this.depth + 1; >+ child.order = this.order + this.numOfLeaves; >+ child.precomputeTree(); >+ child.firstChild = (this.numOfLeaves == 0); >+ child.lastChild = !iterator.hasNext(); >+ >+ this.height = Math.max(this.height, child.height + 1); >+ this.numOfLeaves += child.numOfLeaves; >+ this.numOfDescendants += child.numOfDescendants + 1; >+ } >+ } >+ } >+ >+ /** >+ * Finds a node that is the best parent for this node. Add this node as >+ * a child of the found node. >+ */ >+ protected void findNewParent() { >+ if (parent != null) >+ parent.children.remove(this); >+ NodeLayout[] predecessingNodes = node.getPredecessingNodes(); >+ parent = null; >+ for (int i = 0; i < predecessingNodes.length; i++) { >+ TreeNode potentialParent = (TreeNode) owner.layoutToTree.get(predecessingNodes[i]); >+ if (!children.contains(potentialParent) && isBetterParent(potentialParent)) >+ parent = potentialParent; >+ } >+ if (parent == null) >+ parent = owner.superRoot; >+ >+ parent.addChild(this); >+ } >+ >+ /** >+ * Checks if a potential parent would be better for this node than its >+ * current parent. A better parent has smaller depth (with exception to >+ * {@link TreeLayoutObserver#getSuperRoot() Super Root}, which has depth >+ * equal to -1 but is never a better parent than any other node). >+ * >+ * @param potentialParent >+ * potential parent to check >+ * @return true if potentialParent can be a parent of this node and is >+ * better than its current parent >+ */ >+ protected boolean isBetterParent(TreeNode potentialParent) { >+ if (this.parent == null && !this.isAncestorOf(potentialParent)) >+ return true; >+ if (potentialParent.depth <= this.depth && potentialParent.depth != -1) >+ return true; >+ if (this.parent.depth == -1 && potentialParent.depth >= 0 && !this.isAncestorOf(potentialParent)) >+ return true; >+ return false; >+ } >+ >+ /** >+ * >+ * @param descendant >+ * @return true if this node is an ancestor if given descendant node >+ */ >+ public boolean isAncestorOf(TreeNode descendant) { >+ while (descendant.depth > this.depth) >+ descendant = descendant.parent; >+ return descendant == this; >+ } >+ } >+ >+ /** >+ * A superclass for listeners that can be added to this observer to get >+ * notification whenever the tree structure changes. >+ */ >+ public static class TreeListener { >+ /** >+ * Called when new node is added to the tree structure. The new node >+ * will not have any connections, so it will be a child of >+ * {@link TreeLayoutObserver#getSuperRoot() Super Root} >+ * >+ * @param newNode >+ * the added node >+ */ >+ public void nodeAdded(TreeNode newNode) { >+ defaultHandle(newNode); >+ } >+ >+ /** >+ * Called when a node is removed from the tree structure. The given node >+ * no longer exists in the tree at the moment of call. >+ * >+ * @param removedNode >+ * the removed node >+ */ >+ public void nodeRemoved(TreeNode removedNode) { >+ defaultHandle(removedNode); >+ } >+ >+ /** >+ * Called when a node changes its parent. >+ * >+ * @param node >+ * node that changes its parent >+ * @param previousParent >+ * previous parent of the node >+ */ >+ public void parentChanged(TreeNode node, TreeNode previousParent) { >+ defaultHandle(node); >+ } >+ >+ /** >+ * A convenience method that can be overridden if a listener reacts the >+ * same way to all events. By default it's called in every event handler >+ * and does nothing. >+ * >+ * @param changedNode >+ * the node that has changed >+ */ >+ protected void defaultHandle(TreeNode changedNode) { >+ } >+ } >+ >+ private GraphStructureListener structureListener = new GraphStructureListener() { >+ >+ public boolean nodeRemoved(LayoutContext context, NodeLayout node) { >+ TreeNode treeNode = (TreeNode) layoutToTree.get(node); >+ treeNode.parent.children.remove(treeNode); >+ superRoot.precomputeTree(); >+ for (Iterator iterator = treeListeners.iterator(); iterator.hasNext();) { >+ TreeListener listener = (TreeListener) iterator.next(); >+ listener.nodeRemoved(treeNode); >+ } >+ return false; >+ } >+ >+ public boolean nodeAdded(LayoutContext context, NodeLayout node) { >+ TreeNode treeNode = getTreeNode(node); >+ superRoot.addChild(treeNode); >+ superRoot.precomputeTree(); >+ for (Iterator iterator = treeListeners.iterator(); iterator.hasNext();) { >+ TreeListener listener = (TreeListener) iterator.next(); >+ listener.nodeAdded(treeNode); >+ } >+ return false; >+ } >+ >+ public boolean connectionRemoved(LayoutContext context, ConnectionLayout connection) { >+ TreeNode node1 = (TreeNode) layoutToTree.get(connection.getSource()); >+ TreeNode node2 = (TreeNode) layoutToTree.get(connection.getTarget()); >+ if (node1.parent == node2) { >+ node1.findNewParent(); >+ if (node1.parent != node2) { >+ superRoot.precomputeTree(); >+ fireParentChanged(node1, node2); >+ } >+ } >+ if (node2.parent == node1) { >+ node2.findNewParent(); >+ if (node2.parent != node1) { >+ superRoot.precomputeTree(); >+ fireParentChanged(node2, node1); >+ } >+ } >+ return false; >+ } >+ >+ public boolean connectionAdded(LayoutContext context, ConnectionLayout connection) { >+ TreeNode source = (TreeNode) layoutToTree.get(connection.getSource()); >+ TreeNode target = (TreeNode) layoutToTree.get(connection.getTarget()); >+ if (source == target) >+ return false; >+ if (target.isBetterParent(source)) { >+ TreeNode previousParent = target.parent; >+ previousParent.children.remove(target); >+ source.addChild(target); >+ superRoot.precomputeTree(); >+ fireParentChanged(target, previousParent); >+ } >+ if (!connection.isDirected() && source.isBetterParent(target)) { >+ TreeNode previousParent = source.parent; >+ previousParent.children.remove(source); >+ target.addChild(source); >+ superRoot.precomputeTree(); >+ fireParentChanged(source, previousParent); >+ } >+ return false; >+ } >+ >+ private void fireParentChanged(TreeNode node, TreeNode previousParent) { >+ for (Iterator iterator = treeListeners.iterator(); iterator.hasNext();) { >+ TreeListener listener = (TreeListener) iterator.next(); >+ listener.parentChanged(node, previousParent); >+ } >+ } >+ }; >+ >+ private final HashMap layoutToTree = new HashMap(); >+ private final TreeNodeFactory factory; >+ private final LayoutContext context; >+ private TreeNode superRoot; >+ private ArrayList treeListeners = new ArrayList(); >+ >+ /** >+ * Creates a >+ * >+ * @param context >+ * @param nodeFactory >+ */ >+ public TreeLayoutObserver(LayoutContext context, TreeNodeFactory nodeFactory) { >+ if (nodeFactory == null) >+ this.factory = new TreeNodeFactory(); >+ else >+ this.factory = nodeFactory; >+ this.context = context; >+ context.addGraphStructureListener(structureListener); >+ recomputeTree(); >+ } >+ >+ /** >+ * Recomputes all the information about the tree structure (the same effect >+ * as creating new <code>TreeLayoutObserver</code>). >+ */ >+ public void recomputeTree() { >+ superRoot = factory.createTreeNode(null, this); >+ layoutToTree.put(null, superRoot); >+ createTrees(context.getNodes()); >+ } >+ >+ /** >+ * Stops this observer from listening to changes in observed layout context. >+ * After calling this method the information about tree structure can be >+ * updated only when {@link #recomputeTree()} is called. >+ */ >+ public void stop() { >+ context.removeGraphStructureListener(structureListener); >+ } >+ >+ /** >+ * Returns Super Root, that is an artificial node being a common parent for >+ * all nodes in observed tree structure. >+ * >+ * @return Super Root >+ */ >+ public TreeNode getSuperRoot() { >+ return superRoot; >+ } >+ >+ /** >+ * Returns a {@link TreeNode} related to given node layout. If such a >+ * <code>TreeNode</code> doesn't exist, it's created. >+ * >+ * @param node >+ * @return >+ */ >+ public TreeNode getTreeNode(NodeLayout node) { >+ TreeNode treeNode = (TreeNode) layoutToTree.get(node); >+ if (treeNode == null) { >+ treeNode = factory.createTreeNode(node, this); >+ layoutToTree.put(node, treeNode); >+ } >+ return treeNode; >+ } >+ >+ /** >+ * Adds a listener that will be informed about changes in tree structure. >+ * >+ * @param listener >+ * listener to add >+ */ >+ public void addTreeListener(TreeListener listener) { >+ treeListeners.add(listener); >+ } >+ >+ /** >+ * Removes a listener from list of listener to be informed about changes in >+ * tree structure. >+ * >+ * @param listener >+ * listener to remove >+ */ >+ public void removeTreeListener(TreeListener listener) { >+ treeListeners.add(listener); >+ } >+ >+ /** >+ * Builds a tree structure using BFS method. Created trees are children of >+ * {@link #superRoot}. >+ * >+ * @param nodes >+ */ >+ private void createTrees(NodeLayout[] nodes) { >+ HashSet alreadyVisited = new HashSet(); >+ LinkedList nodesToAdd = new LinkedList(); >+ for (int i = 0; i < nodes.length; i++) { >+ NodeLayout root = findRoot(nodes[i], alreadyVisited); >+ if (root != null) { >+ alreadyVisited.add(root); >+ nodesToAdd.addLast(new Object[] { root, superRoot }); >+ } >+ } >+ while (!nodesToAdd.isEmpty()) { >+ Object[] dequeued = (Object[]) nodesToAdd.removeFirst(); >+ TreeNode currentNode = factory.createTreeNode((NodeLayout) dequeued[0], this); >+ layoutToTree.put(dequeued[0], currentNode); >+ TreeNode currentRoot = (TreeNode) dequeued[1]; >+ >+ currentRoot.addChild(currentNode); >+ NodeLayout[] children = currentNode.node.getSuccessingNodes(); >+ for (int i = 0; i < children.length; i++) { >+ if (!alreadyVisited.contains(children[i])) { >+ alreadyVisited.add(children[i]); >+ nodesToAdd.addLast(new Object[] { children[i], currentNode }); >+ } >+ } >+ } >+ superRoot.precomputeTree(); >+ } >+ >+ /** >+ * Searches for a root of a tree containing given node by continuously >+ * grabbing a predecessor of current node. If it reaches an node that exists >+ * in alreadyVisited set, it returns null. If it detects a cycle, it returns >+ * the first found node of that cycle. If it reaches a node that has no >+ * predecessors, it returns that node. >+ * >+ * @param nodeLayout >+ * starting node >+ * @param alreadyVisited >+ * set of nodes that can't lay on path to the root (if one does, >+ * method stops and returns null). >+ * @return >+ */ >+ private NodeLayout findRoot(NodeLayout nodeLayout, Set alreadyVisited) { >+ HashSet alreadyVisitedRoot = new HashSet(); >+ while (true) { >+ if (alreadyVisited.contains(nodeLayout)) >+ return null; >+ if (alreadyVisitedRoot.contains(nodeLayout)) >+ return nodeLayout; >+ alreadyVisitedRoot.add(nodeLayout); >+ NodeLayout[] predecessingNodes = nodeLayout.getPredecessingNodes(); >+ if (predecessingNodes.length > 0) { >+ nodeLayout = predecessingNodes[0]; >+ } else { >+ return nodeLayout; >+ } >+ } >+ } >+} >Index: src/org/eclipse/zest/layouts/algorithms/VerticalLayoutAlgorithm.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/algorithms/VerticalLayoutAlgorithm.java >diff -N src/org/eclipse/zest/layouts/algorithms/VerticalLayoutAlgorithm.java >--- src/org/eclipse/zest/layouts/algorithms/VerticalLayoutAlgorithm.java 12 Sep 2007 20:44:37 -0000 1.5 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,52 +0,0 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts.algorithms; >- >-import org.eclipse.zest.layouts.LayoutStyles; >- >-/** >- * @version 2.0 >- * @author Casey Best and Rob Lintern (version 1.0 by Rob Lintern) >- */ >-public class VerticalLayoutAlgorithm extends GridLayoutAlgorithm { >- >- >- /** >- * Veertical Layout Algorithm constructor with no styles. >- * >- */ >- public VerticalLayoutAlgorithm() { >- this(LayoutStyles.NONE ); >- } >- >- public VerticalLayoutAlgorithm(int styles) { >- super(styles); >- } >- >- /** >- * Calculates and returns an array containing the number of columns, followed by the number of rows >- */ >- protected int[] calculateNumberOfRowsAndCols (int numChildren, double boundX, double boundY, double boundWidth, double boundHeight) { >- int cols = 1; >- int rows = numChildren; >- int[] result = {cols, rows}; >- return result; >- } >- >- protected boolean isValidConfiguration(boolean asynchronous, boolean continueous) { >- if ( asynchronous && continueous ) return false; >- else if ( asynchronous && !continueous ) return true; >- else if ( !asynchronous && continueous ) return false; >- else if ( !asynchronous && !continueous ) return true; >- >- return false; >- } >-} >Index: src/org/eclipse/zest/layouts/algorithms/internal/CycleChecker.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/algorithms/internal/CycleChecker.java >diff -N src/org/eclipse/zest/layouts/algorithms/internal/CycleChecker.java >--- src/org/eclipse/zest/layouts/algorithms/internal/CycleChecker.java 12 Sep 2007 20:44:38 -0000 1.3 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,119 +0,0 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts.algorithms.internal; >- >-import java.util.ArrayList; >-import java.util.Arrays; >-import java.util.Hashtable; >-import java.util.Iterator; >-import java.util.List; >- >-import org.eclipse.zest.layouts.LayoutEntity; >-import org.eclipse.zest.layouts.LayoutRelationship; >-import org.eclipse.zest.layouts.algorithms.AbstractLayoutAlgorithm; >-import org.eclipse.zest.layouts.exampleStructures.SimpleRelationship; >- >- >-/** >- * Checks for cycles in the given graph. >- * >- * @author Casey Best >- */ >-public class CycleChecker { >- /** >- * Tests if there is a directed cirlce in the graph formed by the given entities and relationships. >- * @param entities The entities in the graph to check >- * @param relationships The relationships in the graph to check >- * @param cycle Populated with the cycle encountered, if there is one. >- * @throws RuntimeException Thrown if entities doesn't contain all of the endpoints for each relationship in relationships >- * @return <code>true</code> if there is a directed circle. >- * Otherwise, <code>false</code>. >- */ >- public static boolean hasDirectedCircles(LayoutEntity[] entities, LayoutRelationship[] relationships, List cycle) { >- if (!AbstractLayoutAlgorithm.verifyInput(entities, relationships)) { >- throw new RuntimeException ("The endpoints of the relationships aren't contained in the entities list."); >- } >- //Enumeration enum; >- //Iterator iterator; >- >- Hashtable endPoints = new Hashtable(); >- >- // Initialize the relation(transitive) vector. >- for (int i = 0; i < relationships.length; i++) { >- LayoutRelationship rel = relationships[i]; >- >- //Add the relationship to the source endpoint >- Object subject = rel.getSourceInLayout(); >- List rels = (List) endPoints.get(subject); >- if (rels == null) { >- rels = new ArrayList(); >- endPoints.put(subject, rels); >- } >- if (!rels.contains(rel)) >- rels.add(rel); >- } >- boolean hasCyle = hasCycle(new ArrayList(Arrays.asList(entities)), endPoints, cycle); >- return hasCyle; >- } >- >- /** >- * Check passed in nodes for a cycle >- */ >- private static boolean hasCycle(List nodesToCheck, Hashtable endPoints, List cycle) { >- while (nodesToCheck.size() > 0) { >- Object checkNode = nodesToCheck.get(0); >- List checkedNodes = new ArrayList(); >- if (hasCycle(checkNode, new ArrayList(), null, endPoints, checkedNodes, cycle)) { >- return true; >- } >- nodesToCheck.removeAll(checkedNodes); >- } >- return false; >- } >- >- /** >- * Checks all the nodes attached to the nodeToCheck node for a cycle. All nodes >- * checked are placed in nodePathSoFar. >- * >- * @returns true if there is a cycle >- */ >- private static boolean hasCycle(Object nodeToCheck, List nodePathSoFar, SimpleRelationship cameFrom, Hashtable endPoints, List nodesChecked, List cycle) { >- if (nodePathSoFar.contains(nodeToCheck)) { >- cycle.addAll(nodePathSoFar); >- cycle.add(nodeToCheck); >- return true; >- } >- nodePathSoFar.add(nodeToCheck); >- nodesChecked.add(nodeToCheck); >- >- List relations = (List) endPoints.get(nodeToCheck); >- if (relations != null) { >- for (Iterator iter = relations.iterator(); iter.hasNext();) { >- SimpleRelationship rel = (SimpleRelationship) iter.next(); >- >- if (cameFrom == null || !rel.equals(cameFrom)) { >- Object currentNode = null; >- currentNode = rel.getDestinationInLayout(); >- if (hasCycle(currentNode, nodePathSoFar, rel, endPoints, nodesChecked, cycle)) { >- return true; >- } >- } >- >- } >- } >- >- nodePathSoFar.remove(nodeToCheck); >- >- return false; >- } >- >-} >Index: src/org/eclipse/zest/layouts/algorithms/internal/DynamicScreen.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/algorithms/internal/DynamicScreen.java >diff -N src/org/eclipse/zest/layouts/algorithms/internal/DynamicScreen.java >--- src/org/eclipse/zest/layouts/algorithms/internal/DynamicScreen.java 12 Sep 2007 20:44:38 -0000 1.3 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,126 +0,0 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts.algorithms.internal; >- >-import java.util.Comparator; >-import java.util.TreeSet; >- >-import org.eclipse.zest.layouts.dataStructures.DisplayIndependentPoint; >-import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle; >-import org.eclipse.zest.layouts.dataStructures.InternalNode; >- >- >-public class DynamicScreen { >- >- private TreeSet XCoords = null; >- private TreeSet YCoords = null; >- >- >- >- private DisplayIndependentRectangle screenBounds = null; >- >- >- double minX = 0.0; >- double minY = 0.0; >- double maxX = 0.0; >- double maxY = 0.0; >- public void cleanScreen() { >- minX = 0.0; >- minY = 0.0; >- maxX = 0.0; >- maxY = 0.0; >- } >- >- class XComparator implements Comparator { >- public int compare(Object arg0, Object arg1) { >- InternalNode n1 = (InternalNode)arg0; >- InternalNode n2 = (InternalNode)arg1; >- if ( n1.getInternalX() > n2.getInternalX() ) return +1; >- else if ( n1.getInternalX() < n2.getInternalX() ) return -1; >- else { >- return n1.toString().compareTo( n2.toString() ); >- } >- >- } >- } >- class YComparator implements Comparator { >- public int compare(Object arg0, Object arg1) { >- InternalNode n1 = (InternalNode)arg0; >- InternalNode n2 = (InternalNode)arg1; >- if ( n1.getInternalY() > n2.getInternalY() ) return +1; >- else if ( n1.getInternalY() < n2.getInternalY() ) return -1; >- else { >- return n1.toString().compareTo( n2.toString() ); >- } >- >- } >- } >- >- >- >- >- public DynamicScreen(int x, int y, int width, int height) { >- XCoords = new TreeSet(new XComparator()); >- YCoords = new TreeSet(new YComparator()); >- >- >- >- this.screenBounds = new DisplayIndependentRectangle(x,y,width,height); >- } >- >- >- public void removeNode( InternalNode node ) { >- XCoords.remove( node ); >- YCoords.remove( node ); >- } >- >- public void addNode( InternalNode node ) { >- XCoords.add( node ); >- YCoords.add( node ); >- } >- >- public DisplayIndependentPoint getScreenLocation( InternalNode node ) { >- >- DisplayIndependentRectangle layoutBounds = calculateBounds(); >- >- double x = (layoutBounds.width == 0) ? 0 : (node.getInternalX() - layoutBounds.x) / layoutBounds.width; >- double y = (layoutBounds.height == 0) ? 0 : (node.getInternalY() - layoutBounds.y) / layoutBounds.height; >- >- x = screenBounds.x + x * screenBounds.width; >- y = screenBounds.y + y * screenBounds.height; >- >- return new DisplayIndependentPoint( x, y ); >- } >- >- public DisplayIndependentPoint getVirtualLocation( DisplayIndependentPoint point ) { >- >- DisplayIndependentRectangle layoutBounds = calculateBounds(); >- >- >- double x = (point.x/screenBounds.width) * layoutBounds.width + layoutBounds.x; >- double y = (point.y/screenBounds.height) * layoutBounds.height + layoutBounds.y; >- >- return new DisplayIndependentPoint( x, y ); >- } >- >- private DisplayIndependentRectangle calculateBounds() { >- InternalNode n1 = (InternalNode) XCoords.first(); >- InternalNode n2 = (InternalNode) XCoords.last(); >- InternalNode n3 = (InternalNode) YCoords.first(); >- InternalNode n4 = (InternalNode) YCoords.last(); >- double x = n1.getInternalX(); >- double width = n2.getInternalX(); >- double y = n3.getInternalY(); >- double height = n4.getInternalY(); >- return new DisplayIndependentRectangle(x, y, width - x, height - y); >- } >- >-} >Index: src/org/eclipse/zest/layouts/constraints/BasicEdgeConstraints.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/constraints/BasicEdgeConstraints.java >diff -N src/org/eclipse/zest/layouts/constraints/BasicEdgeConstraints.java >--- src/org/eclipse/zest/layouts/constraints/BasicEdgeConstraints.java 12 Sep 2007 20:44:38 -0000 1.3 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,32 +0,0 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts.constraints; >- >-/** >- * @author Ian Bull >- * @author Chris Bennett >- */ >-public class BasicEdgeConstraints implements LayoutConstraint { >- >- // These should all be accessed directly. >- public boolean isBiDirectional = false; >- public int weight = 1; >- >- /* >- * (non-Javadoc) >- * @see org.eclipse.zest.layouts.constraints.LayoutConstraint#clear() >- */ >- public void clear() { >- this.isBiDirectional = false; >- this.weight = 1; >- } >- >-} >Index: src/org/eclipse/zest/layouts/constraints/BasicEntityConstraint.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/constraints/BasicEntityConstraint.java >diff -N src/org/eclipse/zest/layouts/constraints/BasicEntityConstraint.java >--- src/org/eclipse/zest/layouts/constraints/BasicEntityConstraint.java 12 Sep 2007 20:44:38 -0000 1.3 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,46 +0,0 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts.constraints; >- >-/** >- * >- * @author Chris Bennett >- * >- */ >-public class BasicEntityConstraint implements LayoutConstraint { >- >- >- public boolean hasPreferredLocation = false; >- >- public double preferredX; >- public double preferredY; >- >- public boolean hasPreferredSize = false; >- public double preferredWidth; >- public double preferredHeight; >- >- public BasicEntityConstraint() { >- clear(); >- } >- >- /* >- * (non-Javadoc) >- * @see org.eclipse.zest.layouts.constraints.LayoutConstraint#clear() >- */ >- public void clear() { >- this.hasPreferredLocation = false; >- this.hasPreferredSize = false; >- this.preferredX = 0.0; >- this.preferredY = 0.0; >- this.preferredWidth = 0.0; >- this.preferredHeight = 0.0; >- } >-} >Index: src/org/eclipse/zest/layouts/constraints/EntityPriorityConstraint.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/constraints/EntityPriorityConstraint.java >diff -N src/org/eclipse/zest/layouts/constraints/EntityPriorityConstraint.java >--- src/org/eclipse/zest/layouts/constraints/EntityPriorityConstraint.java 12 Sep 2007 20:44:38 -0000 1.3 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,31 +0,0 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts.constraints; >- >-/** >- * A layout constraint that uses priorities >- * @author Ian Bull >- */ >-public class EntityPriorityConstraint implements LayoutConstraint { >- >- // A priority that can be set for nodes. This could be used >- // for a treemap layout >- public double priority = 1.0; >- >- /* >- * (non-Javadoc) >- * @see org.eclipse.zest.layouts.constraints.LayoutConstraint#clear() >- */ >- public void clear() { >- this.priority = 1.0; >- } >- >-} >Index: src/org/eclipse/zest/layouts/constraints/LabelLayoutConstraint.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/constraints/LabelLayoutConstraint.java >diff -N src/org/eclipse/zest/layouts/constraints/LabelLayoutConstraint.java >--- src/org/eclipse/zest/layouts/constraints/LabelLayoutConstraint.java 12 Sep 2007 20:44:38 -0000 1.3 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,33 +0,0 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts.constraints; >- >-/** >- * @author Ian Bull >- * @author Chris Bennett >- */ >-public class LabelLayoutConstraint implements LayoutConstraint { >- >- // These should be accessed directly >- public String label; >- public int pointSize; >- >- /* >- * (non-Javadoc) >- * @see org.eclipse.zest.layouts.constraints.LayoutConstraint#clear() >- */ >- public void clear() { >- label = null; >- pointSize = 1; >- } >- >-} >- >Index: src/org/eclipse/zest/layouts/constraints/LayoutConstraint.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/constraints/LayoutConstraint.java >diff -N src/org/eclipse/zest/layouts/constraints/LayoutConstraint.java >--- src/org/eclipse/zest/layouts/constraints/LayoutConstraint.java 12 Sep 2007 20:44:38 -0000 1.4 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,26 +0,0 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts.constraints; >- >-/** >- * @author Ian Bull >- * @author Chris Bennett >- */ >-public interface LayoutConstraint { >- >- // Empty interface >- >- /** >- * This method clears all the fields of the layout constraints. >- * This should not be called outside the layout package >- */ >- public void clear(); >-} >Index: src/org/eclipse/zest/layouts/dataStructures/BendPoint.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/dataStructures/BendPoint.java >diff -N src/org/eclipse/zest/layouts/dataStructures/BendPoint.java >--- src/org/eclipse/zest/layouts/dataStructures/BendPoint.java 12 Sep 2007 20:44:37 -0000 1.5 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,54 +0,0 @@ >-/******************************************************************************* >- * Copyright 2006, 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.layouts.dataStructures; >- >-import org.eclipse.zest.layouts.LayoutBendPoint; >- >-/** >- * Implements a single bend point in a graph relationship. >- * >- * @author Ian Bull >- * @author Chris Bennett >- */ >-public class BendPoint extends DisplayIndependentPoint implements LayoutBendPoint { >- >- private boolean isControlPoint = false; // is this a control point (for use in curves) >- >- public BendPoint(double x, double y) { >- super(x, y); >- } >- >- public BendPoint(double x, double y, boolean isControlPoint) { >- this(x, y); >- this.isControlPoint = isControlPoint; >- } >- >- public double getX() { >- return x; >- } >- >- public double getY() { >- return y; >- } >- >- public void setX(double x) { >- this.x = x; >- } >- >- public void setY(double y) { >- this.y = y; >- } >- >- public boolean getIsControlPoint() { >- return isControlPoint; >- } >- >-} >Index: src/org/eclipse/zest/layouts/dataStructures/DisplayIndependentDimension.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.layouts/src/org/eclipse/zest/layouts/dataStructures/DisplayIndependentDimension.java,v >retrieving revision 1.3 >diff -u -r1.3 DisplayIndependentDimension.java >--- src/org/eclipse/zest/layouts/dataStructures/DisplayIndependentDimension.java 12 Sep 2007 20:44:37 -0000 1.3 >+++ src/org/eclipse/zest/layouts/dataStructures/DisplayIndependentDimension.java 15 Mar 2010 03:49:09 -0000 >@@ -1,35 +1,35 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts.dataStructures; >- >-/** >- * This is a dimension that isn't dependent on awt, swt, or any other library, >- * except layout. >- * >- * @author Casey Best >- */ >-public class DisplayIndependentDimension { >- public double width, height; >- >- public DisplayIndependentDimension (double width, double height) { >- this.width = width; >- this.height = height; >- } >- >- public DisplayIndependentDimension (DisplayIndependentDimension dimension) { >- this.width = dimension.width; >- this.height = dimension.height; >- } >- >- public String toString() { >- return "(" + width + ", " + height + ")"; >- } >-} >+/******************************************************************************* >+ * Copyright (c) 2005-2010 The Chisel Group and others. 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 - initial API and implementation >+ * Mateusz Matela >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.layouts.dataStructures; >+ >+/** >+ * This is a dimension that isn't dependent on awt, swt, or any other library, >+ * except layout. >+ * >+ * @author Casey Best >+ */ >+public class DisplayIndependentDimension { >+ public double width, height; >+ >+ public DisplayIndependentDimension (double width, double height) { >+ this.width = width; >+ this.height = height; >+ } >+ >+ public DisplayIndependentDimension (DisplayIndependentDimension dimension) { >+ this.width = dimension.width; >+ this.height = dimension.height; >+ } >+ >+ public String toString() { >+ return "(" + width + ", " + height + ")"; >+ } >+} >Index: src/org/eclipse/zest/layouts/dataStructures/DisplayIndependentPoint.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.layouts/src/org/eclipse/zest/layouts/dataStructures/DisplayIndependentPoint.java,v >retrieving revision 1.4 >diff -u -r1.4 DisplayIndependentPoint.java >--- src/org/eclipse/zest/layouts/dataStructures/DisplayIndependentPoint.java 12 Sep 2007 20:44:37 -0000 1.4 >+++ src/org/eclipse/zest/layouts/dataStructures/DisplayIndependentPoint.java 15 Mar 2010 03:49:09 -0000 >@@ -1,84 +1,84 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts.dataStructures; >- >-/** >- * This is a point that isn't dependent on awt, swt, or any other library, >- * except layout. >- * >- * @author Casey Best >- */ >-public class DisplayIndependentPoint { >- public double x, y; >- >- >- public boolean equals( Object o ) { >- DisplayIndependentPoint that = (DisplayIndependentPoint) o; >- if ( this.x == that.x && this.y == that.y ) return true; >- else return false; >- } >- >- public DisplayIndependentPoint (double x, double y) { >- this.x = x; >- this.y = y; >- } >- >- public DisplayIndependentPoint (DisplayIndependentPoint point) { >- this.x = point.x; >- this.y = point.y; >- } >- >- public String toString() { >- return "(" + x + ", " + y + ")"; >- } >- >- /** >- * Create a new point based on the current point but in a new coordinate system >- * @param currentBounds >- * @param targetBounds >- * @return >- */ >- public DisplayIndependentPoint convert(DisplayIndependentRectangle currentBounds , >- DisplayIndependentRectangle targetBounds) { >- double currentWidth = currentBounds.width; >- double currentHeight = currentBounds.height; >- >- double newX = (currentBounds.width == 0) ? 0 : >- (x / currentWidth) * targetBounds.width + targetBounds.x; >- double newY = (currentBounds.height == 0) ? 0 : >- (y / currentHeight) * targetBounds.height + targetBounds.y; >- return new DisplayIndependentPoint( newX, newY ); >- } >- >- /** >- * Converts this point based on the current x, y values to a percentage >- * of the specified coordinate system >- * @param bounds >- * @return >- */ >- public DisplayIndependentPoint convertToPercent(DisplayIndependentRectangle bounds) { >- double newX = (bounds.width == 0) ? 0 : ((double)(x - bounds.x))/ bounds.width; >- double newY = (bounds.height == 0) ? 0 : ((double)(y - bounds.y))/bounds.height; >- return new DisplayIndependentPoint(newX, newY); >- } >- >- /** >- * Converts this point based on the current x, y values from a percentage >- * of the specified coordinate system >- * @param bounds >- * @return >- */ >- public DisplayIndependentPoint convertFromPercent(DisplayIndependentRectangle bounds) { >- double newX = bounds.x + x * bounds.width; >- double newY = bounds.y + y * bounds.height; >- return new DisplayIndependentPoint(newX, newY); >- } >-} >+/******************************************************************************* >+ * Copyright (c) 2005-2010 The Chisel Group and others. 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 - initial API and implementation >+ * Mateusz Matela >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.layouts.dataStructures; >+ >+/** >+ * This is a point that isn't dependent on awt, swt, or any other library, >+ * except layout. >+ * >+ * @author Casey Best >+ */ >+public class DisplayIndependentPoint { >+ public double x, y; >+ >+ >+ public boolean equals( Object o ) { >+ DisplayIndependentPoint that = (DisplayIndependentPoint) o; >+ if ( this.x == that.x && this.y == that.y ) return true; >+ else return false; >+ } >+ >+ public DisplayIndependentPoint (double x, double y) { >+ this.x = x; >+ this.y = y; >+ } >+ >+ public DisplayIndependentPoint (DisplayIndependentPoint point) { >+ this.x = point.x; >+ this.y = point.y; >+ } >+ >+ public String toString() { >+ return "(" + x + ", " + y + ")"; >+ } >+ >+ /** >+ * Create a new point based on the current point but in a new coordinate system >+ * @param currentBounds >+ * @param targetBounds >+ * @return >+ */ >+ public DisplayIndependentPoint convert(DisplayIndependentRectangle currentBounds , >+ DisplayIndependentRectangle targetBounds) { >+ double currentWidth = currentBounds.width; >+ double currentHeight = currentBounds.height; >+ >+ double newX = (currentBounds.width == 0) ? 0 : >+ (x / currentWidth) * targetBounds.width + targetBounds.x; >+ double newY = (currentBounds.height == 0) ? 0 : >+ (y / currentHeight) * targetBounds.height + targetBounds.y; >+ return new DisplayIndependentPoint( newX, newY ); >+ } >+ >+ /** >+ * Converts this point based on the current x, y values to a percentage >+ * of the specified coordinate system >+ * @param bounds >+ * @return >+ */ >+ public DisplayIndependentPoint convertToPercent(DisplayIndependentRectangle bounds) { >+ double newX = (bounds.width == 0) ? 0 : ((double)(x - bounds.x))/ bounds.width; >+ double newY = (bounds.height == 0) ? 0 : ((double)(y - bounds.y))/bounds.height; >+ return new DisplayIndependentPoint(newX, newY); >+ } >+ >+ /** >+ * Converts this point based on the current x, y values from a percentage >+ * of the specified coordinate system >+ * @param bounds >+ * @return >+ */ >+ public DisplayIndependentPoint convertFromPercent(DisplayIndependentRectangle bounds) { >+ double newX = bounds.x + x * bounds.width; >+ double newY = bounds.y + y * bounds.height; >+ return new DisplayIndependentPoint(newX, newY); >+ } >+} >Index: src/org/eclipse/zest/layouts/dataStructures/DisplayIndependentRectangle.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.layouts/src/org/eclipse/zest/layouts/dataStructures/DisplayIndependentRectangle.java,v >retrieving revision 1.4 >diff -u -r1.4 DisplayIndependentRectangle.java >--- src/org/eclipse/zest/layouts/dataStructures/DisplayIndependentRectangle.java 12 Sep 2007 20:44:37 -0000 1.4 >+++ src/org/eclipse/zest/layouts/dataStructures/DisplayIndependentRectangle.java 15 Mar 2010 03:49:09 -0000 >@@ -1,55 +1,55 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts.dataStructures; >- >- >-/** >- * This is a rectangle that isn't dependent on awt, swt, or any other library, >- * except layout. >- * >- * @author Casey Best >- */ >-public class DisplayIndependentRectangle { >- >- public double x, y, width, height; >- >- public DisplayIndependentRectangle() { >- this.x = 0; >- this.y = 0; >- this.width = 0; >- this.height = 0; >- } >- >- public DisplayIndependentRectangle(double x, double y, double width, double height) { >- this.x = x; >- this.y = y; >- this.width = width; >- this.height = height; >- } >- >- public DisplayIndependentRectangle(DisplayIndependentRectangle rect) { >- this.x = rect.x; >- this.y = rect.y; >- this.width = rect.width; >- this.height = rect.height; >- } >- >- public String toString() { >- return "(" + x + ", " + y + ", " + width + ", " + height + ")"; >- } >- >- public boolean intersects(DisplayIndependentRectangle rect) { >- return rect.x < x + width >- && rect.y < y + height >- && rect.x + rect.width > x >- && rect.y + rect.height > y; >- } >-} >+/******************************************************************************* >+ * Copyright (c) 2005-2010 The Chisel Group and others. 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 - initial API and implementation >+ * Mateusz Matela >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.layouts.dataStructures; >+ >+ >+/** >+ * This is a rectangle that isn't dependent on awt, swt, or any other library, >+ * except layout. >+ * >+ * @author Casey Best >+ */ >+public class DisplayIndependentRectangle { >+ >+ public double x, y, width, height; >+ >+ public DisplayIndependentRectangle() { >+ this.x = 0; >+ this.y = 0; >+ this.width = 0; >+ this.height = 0; >+ } >+ >+ public DisplayIndependentRectangle(double x, double y, double width, double height) { >+ this.x = x; >+ this.y = y; >+ this.width = width; >+ this.height = height; >+ } >+ >+ public DisplayIndependentRectangle(DisplayIndependentRectangle rect) { >+ this.x = rect.x; >+ this.y = rect.y; >+ this.width = rect.width; >+ this.height = rect.height; >+ } >+ >+ public String toString() { >+ return "(" + x + ", " + y + ", " + width + ", " + height + ")"; >+ } >+ >+ public boolean intersects(DisplayIndependentRectangle rect) { >+ return rect.x < x + width >+ && rect.y < y + height >+ && rect.x + rect.width > x >+ && rect.y + rect.height > y; >+ } >+} >Index: src/org/eclipse/zest/layouts/dataStructures/InternalNode.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/dataStructures/InternalNode.java >diff -N src/org/eclipse/zest/layouts/dataStructures/InternalNode.java >--- src/org/eclipse/zest/layouts/dataStructures/InternalNode.java 12 Jan 2008 01:44:03 -0000 1.7 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,240 +0,0 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts.dataStructures; >- >-import java.util.HashMap; >- >-import org.eclipse.zest.layouts.LayoutEntity; >-import org.eclipse.zest.layouts.constraints.BasicEntityConstraint; >-import org.eclipse.zest.layouts.constraints.LayoutConstraint; >- >-/** >- * @author Ian Bull >- */ >-public class InternalNode implements Comparable, LayoutEntity { >- >- private LayoutEntity entity = null; >- private HashMap attributeMap = new HashMap(); >- BasicEntityConstraint basicEntityConstraint = new BasicEntityConstraint(); >- >- public InternalNode( LayoutEntity entity ) { >- this.entity = entity; >- this.entity.setLayoutInformation(this); >- this.layoutWidth = entity.getWidthInLayout(); >- this.layoutHeight = entity.getHeightInLayout(); >- entity.populateLayoutConstraint(basicEntityConstraint); >- } >- >- public LayoutEntity getLayoutEntity() { >- return this.entity; >- } >- >- public double getPreferredX() { >- return basicEntityConstraint.preferredX; >- >- } >- >- public double getPreferredY() { >- return basicEntityConstraint.preferredY; >- } >- >- public boolean hasPreferredLocation() { >- return basicEntityConstraint.hasPreferredLocation; >- } >- >- double dx, dy; >- public void setDx( double x ) { >- this.dx = x; >- } >- public void setDy( double y ) { >- this.dy = y; >- } >- public double getDx() { >- return this.dx; >- } >- public double getDy() { >- return this.dy; >- } >- >- public double getCurrentX() { >- return entity.getXInLayout(); >- } >- public double getCurrentY() { >- return entity.getYInLayout(); >- } >- >- public void setLocation( double x, double y ) { >- entity.setLocationInLayout( x, y ); >- } >- public void setSize( double width, double height ) { >- entity.setSizeInLayout( width, height ); >- } >- >- double normalizedX = 0.0; >- double normalizedY = 0.0; >- double normalizedWidth = 0.0; >- double normalizedHeight = 0.0; >- >- public void setInternalLocation( double x, double y ) { >- //entity.setLocationInLayout(x,y); >- >- normalizedX = x; >- normalizedY = y; >- >- } >- >- public DisplayIndependentPoint getInternalLocation() { >- return new DisplayIndependentPoint(getInternalX(),getInternalY()); >- } >- >- public void setInternalSize( double width, double height ) { >- normalizedWidth = width; >- normalizedHeight = height; >- } >- >- public double getInternalX() { >- //return entity.getXInLayout(); >- return normalizedX; >- } >- public double getInternalY() { >- //return entity.getYInLayout(); >- return normalizedY; >- } >- public double getInternalWidth() { >- return normalizedWidth; >- } >- public double getInternalHeight() { >- return normalizedHeight; >- } >- >- >- /** >- * An algorithm may require a place to store information. Use this structure for that purpose. >- */ >- public void setAttributeInLayout (Object attribute, Object value) { >- attributeMap.put(attribute, value); >- } >- >- /** >- * An algorithm may require a place to store information. Use this structure for that purpose. >- */ >- public Object getAttributeInLayout (Object attribute) { >- return attributeMap.get( attribute ); >- } >- >- >- //TODO: Fix all these preferred stuff!!!!! NOW! >- >- public boolean hasPreferredWidth () { >- return false; >- //return enity.getAttributeInLayout(LayoutEntity.ATTR_PREFERRED_WIDTH) != null; >- } >- >- public double getPreferredWidth () { >- return 0.0; >-// if (hasPreferredWidth()) { >-// return ((Double)entity.getAttributeInLayout(LayoutEntity.ATTR_PREFERRED_WIDTH)).doubleValue(); >-// } else { >-// return 10.0; >-// } >- } >- >- public boolean hasPreferredHeight () { >- return false; >- // return entity.getAttributeInLayout(LayoutEntity.ATTR_PREFERRED_HEIGHT) != null; >- } >- >- public double getPreferredHeight () { >- return 0.0; >-// if (hasPreferredHeight()) { >-// return ((Double)entity.getAttributeInLayout(LayoutEntity.ATTR_PREFERRED_HEIGHT)).doubleValue(); >-// } else { >-// return 10.0; >-// } >- } >- >- /* (non-Javadoc) >- * @see java.lang.Comparable#compareTo(java.lang.Object) >- */ >- public int compareTo(Object arg0) { >- return 0; >- } >- >- /* (non-Javadoc) >- * @see java.lang.Object#toString() >- */ >- public String toString() { >- return (entity != null ? entity.toString() : ""); >- } >- >- double layoutHeight; >- double layoutWidth; >- double layoutX; >- double layoutY; >- Object layoutInfo; >- >- public double getHeightInLayout() { >- // TODO Auto-generated method stub >- return layoutHeight; >- } >- >- public Object getLayoutInformation() { >- // TODO Auto-generated method stub >- return this.layoutInfo; >- } >- >- public double getWidthInLayout() { >- // TODO Auto-generated method stub >- return layoutWidth; >- } >- >- public double getXInLayout() { >- // TODO Auto-generated method stub >- return layoutX; >- } >- >- public double getYInLayout() { >- // TODO Auto-generated method stub >- return layoutY; >- } >- >- public void populateLayoutConstraint(LayoutConstraint constraint) { >- // TODO Auto-generated method stub >- >- } >- >- public void setLayoutInformation(Object internalEntity) { >- this.layoutInfo = internalEntity; >- >- } >- >- public void setLocationInLayout(double x, double y) { >- // TODO Auto-generated method stub >- this.layoutX = x; >- this.layoutY = y; >- >- } >- >- public void setSizeInLayout(double width, double height) { >- this.layoutWidth = width; >- this.layoutHeight = height; >- } >- >- public Object getGraphData() { >- return null; >- } >- >- public void setGraphData(Object o) { >- // TODO Auto-generated method stub >- >- } >- >-} >Index: src/org/eclipse/zest/layouts/dataStructures/InternalRelationship.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/dataStructures/InternalRelationship.java >diff -N src/org/eclipse/zest/layouts/dataStructures/InternalRelationship.java >--- src/org/eclipse/zest/layouts/dataStructures/InternalRelationship.java 12 Jan 2008 01:44:03 -0000 1.10 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,138 +0,0 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts.dataStructures; >- >-import java.util.LinkedList; >-import java.util.List; >- >-import org.eclipse.zest.layouts.LayoutBendPoint; >-import org.eclipse.zest.layouts.LayoutEntity; >-import org.eclipse.zest.layouts.LayoutRelationship; >-import org.eclipse.zest.layouts.constraints.BasicEdgeConstraints; >-import org.eclipse.zest.layouts.constraints.LayoutConstraint; >- >-/** >- * @author Ian Bull >- */ >-public class InternalRelationship implements LayoutRelationship{ >- >- private LayoutRelationship externalRelationship; >- private InternalNode source; >- private InternalNode destination; >- private Object layoutInfo; >- private List bendPoints = new LinkedList(); >- BasicEdgeConstraints basicEdgeConstraints = new BasicEdgeConstraints(); >- >- public InternalRelationship( LayoutRelationship externalRelationship, InternalNode source, InternalNode destination) { >- this.externalRelationship = externalRelationship; >- this.externalRelationship.setLayoutInformation(this); >- this.source = source; >- this.destination = destination; >- this.externalRelationship.populateLayoutConstraint(basicEdgeConstraints); >- } >- >- public LayoutRelationship getLayoutRelationship() { >- return externalRelationship; >- } >- >- public InternalNode getSource() { >- if ( this.source == null ) { >- throw new RuntimeException("Source is null"); >- } >- return this.source; >- } >- >- public InternalNode getDestination() { >- if ( this.destination == null ) { >- throw new RuntimeException("Dest is null"); >- } >- return this.destination; >- } >- >- public double getWeight() { >- return this.basicEdgeConstraints.weight; >- } >- >- public boolean isBidirectional() { >- return this.basicEdgeConstraints.isBiDirectional; >- } >- >- /** >- * Ensure this is called in order of source to target node position. >- * @param x >- * @param y >- */ >- public void addBendPoint(double x, double y) { >- bendPoints.add(new BendPoint(x, y)); >- } >- >- /** >- * Ensure this is called in order of source to target node position. >- * Specifies if the bendpoint is a curve control point >- * @param x >- * @param y >- * @param isControlPoint >- */ >- public void addBendPoint(double x, double y, boolean isControlPoint) { >- bendPoints.add(new BendPoint(x, y, isControlPoint)); >- } >- >- public List getBendPoints() { >- return this.bendPoints; >- } >- >- public void clearBendPoints() { >- // TODO Auto-generated method stub >- >- } >- >- >- >- public LayoutEntity getDestinationInLayout() { >- // TODO Auto-generated method stub >- return destination; >- } >- >- >- public Object getLayoutInformation() { >- // TODO Auto-generated method stub >- return layoutInfo; >- } >- >- public LayoutEntity getSourceInLayout() { >- // TODO Auto-generated method stub >- return source; >- } >- >- public void populateLayoutConstraint(LayoutConstraint constraint) { >- // TODO Auto-generated method stub >- >- } >- >- public void setBendPoints(LayoutBendPoint[] bendPoints) { >- // TODO Auto-generated method stub >- >- } >- >- public void setLayoutInformation(Object layoutInformation) { >- this.layoutInfo = layoutInformation; >- >- } >- >- public Object getGraphData() { >- return null; >- } >- >- public void setGraphData(Object o) { >- >- } >- >-} >Index: src/org/eclipse/zest/layouts/exampleStructures/SimpleFilter.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/exampleStructures/SimpleFilter.java >diff -N src/org/eclipse/zest/layouts/exampleStructures/SimpleFilter.java >--- src/org/eclipse/zest/layouts/exampleStructures/SimpleFilter.java 12 Jan 2008 01:44:24 -0000 1.4 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,30 +0,0 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts.exampleStructures; >- >-import org.eclipse.zest.layouts.Filter; >-import org.eclipse.zest.layouts.LayoutItem; >- >-/** >- * A very simple example of a filter. This filter never filters >- * any object. >- * >- * @author Casey Best >- */ >-public class SimpleFilter implements Filter { >- >- /** >- * Doesn't filter anything >- */ >- public boolean isObjectFiltered(LayoutItem object) { >- return false; >- } >-} >Index: src/org/eclipse/zest/layouts/exampleStructures/SimpleGraph.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/exampleStructures/SimpleGraph.java >diff -N src/org/eclipse/zest/layouts/exampleStructures/SimpleGraph.java >--- src/org/eclipse/zest/layouts/exampleStructures/SimpleGraph.java 12 Sep 2007 20:44:37 -0000 1.4 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,140 +0,0 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts.exampleStructures; >- >-import java.util.ArrayList; >-import java.util.HashMap; >-import java.util.Iterator; >-import java.util.List; >-import java.util.Map; >- >-import org.eclipse.zest.layouts.LayoutEntity; >-import org.eclipse.zest.layouts.LayoutGraph; >-import org.eclipse.zest.layouts.LayoutRelationship; >- >- >-/** >- * Create a very simple graph that can be used in the layout algorithms >- * >- * @author Casey Best >- * @author Chris Callendar >- */ >-public class SimpleGraph implements LayoutGraph { >- >- Map objectsToNodes; >- List relationships; >- >- public SimpleGraph() { >- objectsToNodes = new HashMap(); >- relationships = new ArrayList(); >- } >- >- /** >- * Adds the node. >- * @param node The node to add. >- */ >- public void addEntity(LayoutEntity node) { >- if (node instanceof SimpleNode) { >- objectsToNodes.put(((SimpleNode)node).getRealObject(), node); >- } >- } >- >- /** >- * Creates a LayoutEntity containing an object. >- */ >- public LayoutEntity addObjectNode(Object object) { >- SimpleNode simpleNode = (SimpleNode) objectsToNodes.get (object); >- if (simpleNode == null) { >- simpleNode = new SimpleNode(object); >- objectsToNodes.put (object, simpleNode); >- } >- return simpleNode; >- >- } >- >- /** >- * Add a relationship between two objects. Layout algorithms need to know >- * whether a relationship is one way or bi-directional. This method assumes that >- * all relationships are bi-direcional and have the same weight. >- */ >- public void addObjectRelationship (Object sourceNode, Object destinationNode) { >- addObjectRelationship(sourceNode, destinationNode, true, 1); >- } >- >- /** >- * Add a relationship between two objects. Layout algorithms need to know >- * whether a relationship is one way or bi-directional. >- */ >- public void addObjectRelationship (Object sourceObject, Object destinationObject, boolean bidirectional, int weight) { >- addObjectNode(sourceObject); >- addObjectNode(destinationObject); >- SimpleNode sourceNode = (SimpleNode) objectsToNodes.get(sourceObject); >- SimpleNode destinationNode = (SimpleNode) objectsToNodes.get(destinationObject); >- SimpleRelationship simpleRelationship = new SimpleRelationship(sourceNode, destinationNode, bidirectional, weight); >- relationships.add(simpleRelationship); >- } >- >- /* (non-Javadoc) >- * @see ca.uvic.cs.chisel.layouts.LayoutGraph#addRelationship(ca.uvic.cs.chisel.layouts.LayoutEntity, ca.uvic.cs.chisel.layouts.LayoutEntity) >- */ >- public void addRelationship(LayoutEntity srcNode, LayoutEntity destNode) { >- addRelationship(srcNode, destNode, true, 1); >- } >- >- /* (non-Javadoc) >- * @see ca.uvic.cs.chisel.layouts.LayoutGraph#addRelationship(ca.uvic.cs.chisel.layouts.LayoutEntity, ca.uvic.cs.chisel.layouts.LayoutEntity, boolean, int) >- */ >- public void addRelationship(LayoutEntity srcNode, LayoutEntity destNode, boolean bidirectional, int weight) { >- addEntity(srcNode); >- addEntity(destNode); >- SimpleRelationship rel = new SimpleRelationship(srcNode, destNode, bidirectional, weight); >- relationships.add(rel); >- } >- >- /* (non-Javadoc) >- * @see ca.uvic.cs.chisel.layouts.LayoutGraph#addRelationship(ca.uvic.cs.chisel.layouts.LayoutRelationship) >- */ >- public void addRelationship(LayoutRelationship relationship) { >- relationships.add(relationship); >- } >- >- /** >- * Returns a list of SimpleNodes that represent the objects added to this graph using addNode. Note that >- * any manipulation to this graph was done on the SimpleNodes, not the real objects. You >- * must still manipulate them yourself. >- */ >- public List getEntities() { >- return new ArrayList (objectsToNodes.values()); >- } >- >- /** >- * Returns a list of SimpleRelationships that represent the objects added to this graph using addRelationship. >- */ >- public List getRelationships() { >- return relationships; >- } >- >- /** >- * Checks the relationships to see if they are all bidirectional. >- * @return boolean if all edges are bidirectional. >- */ >- public boolean isBidirectional() { >- boolean isBidirectional = true; >- for (Iterator iter = relationships.iterator(); iter.hasNext(); ) { >- SimpleRelationship rel = (SimpleRelationship) iter.next(); >- if (!rel.isBidirectionalInLayout()) { >- isBidirectional = false; >- break; >- } >- } >- return isBidirectional; >- } >-} >Index: src/org/eclipse/zest/layouts/exampleStructures/SimpleNode.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/exampleStructures/SimpleNode.java >diff -N src/org/eclipse/zest/layouts/exampleStructures/SimpleNode.java >--- src/org/eclipse/zest/layouts/exampleStructures/SimpleNode.java 12 Jan 2008 01:44:24 -0000 1.9 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,318 +0,0 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts.exampleStructures; >- >-import java.util.Comparator; >-import java.util.HashMap; >-import java.util.LinkedList; >-import java.util.List; >-import java.util.Map; >-import java.util.TreeSet; >- >-import org.eclipse.zest.layouts.LayoutEntity; >-import org.eclipse.zest.layouts.constraints.BasicEntityConstraint; >-import org.eclipse.zest.layouts.constraints.EntityPriorityConstraint; >-import org.eclipse.zest.layouts.constraints.LabelLayoutConstraint; >-import org.eclipse.zest.layouts.constraints.LayoutConstraint; >- >-/** >- * Rerpresents a simple node that can be used in the layout algorithms. >- * >- * @author Ian Bull >- * @author Casey Best (Version 1 by Rob Lintern) >- * @version 2 >- */ >-public class SimpleNode implements LayoutEntity { >- private static Object NODE_NORMAL_COLOR; >- private static Object NODE_SELECTED_COLOR; >- private static Object NODE_ADJACENT_COLOR; >- private static Object BORDER_NORMAL_COLOR; >- private static Object BORDER_SELECTED_COLOR; >- private static Object BORDER_ADJACENT_COLOR; >- >- private static final int BORDER_NORMAL_STROKE = 1; >- private static final int BORDER_STROKE_SELECTED = 2; >- >- /** >- * A list of layout dependent attributes >- */ >- private Map attributes; >- >- protected double x, y, width, height; >- protected Object realObject; >- private boolean ignoreInLayout = false; >- >- private Object colour = null; >- private Object borderColor = null; >- private int borderWidth; >- >- private TreeSet listOfRels = null; >- >- private Object internalNode; >- >- >- /** >- * Constructs a new SimpleNode. >- */ >- public SimpleNode(Object realObject) { >- this (realObject, -1, -1, 110, 110); >- } >- >- class UniqueCompare implements Comparator { >- public int compare(Object o1, Object o2) { >- // TODO this may not always be a unique comparison >- return o1.toString().compareTo( o2.toString() ); >- } >- } >- >- /** >- * Constructs a new SimpleNode. >- */ >- public SimpleNode(Object realObject, double x, double y, double width, double height) { >- this.realObject = realObject; >- this.x = x; >- this.y = y; >- this.width = width; >- this.height = height; >- this.attributes = new HashMap(); >- this.borderWidth = BORDER_NORMAL_STROKE; >- listOfRels = new TreeSet( new UniqueCompare() ); >- this.colour = NODE_NORMAL_COLOR; >- this.borderColor = BORDER_NORMAL_COLOR; >- } >- >- public static void setNodeColors (Object nodeNormalColor, Object borderNormalColor, Object nodeSelectedColor, Object nodeAdjacentColor, Object borderSelectedColor, Object borderAdjacentColor) { >- NODE_NORMAL_COLOR = nodeNormalColor; >- BORDER_NORMAL_COLOR = borderNormalColor; >- NODE_SELECTED_COLOR = nodeSelectedColor; >- NODE_ADJACENT_COLOR = nodeAdjacentColor; >- BORDER_SELECTED_COLOR = borderSelectedColor; >- BORDER_ADJACENT_COLOR = borderAdjacentColor; >- } >- >- public void addRelationship( SimpleRelationship rel ) { >- listOfRels.add( rel ); >- } >- >- >- public SimpleRelationship[] getRelationships() { >- int size = listOfRels.size(); >- return (SimpleRelationship[]) this.listOfRels.toArray( new SimpleRelationship[ size ] ); >- } >- >- public List getRelatedEntities() { >- int size = listOfRels.size(); >- SimpleRelationship[] a_listOfRels = (SimpleRelationship[]) this.listOfRels.toArray( new SimpleRelationship[ size ] ); >- LinkedList listOfRelatedEntities = new LinkedList(); >- for (int i = 0; i < a_listOfRels.length; i++) { >- SimpleRelationship rel = a_listOfRels[ i ]; >- if ( rel.sourceEntity != this && rel.destinationEntity != this ) { >- throw new RuntimeException("Problem, we have a relationship and we are not the source or the dest"); >- } >- if ( rel.sourceEntity != this ) { >- listOfRelatedEntities.add( rel.sourceEntity ); >- } >- if ( rel.destinationEntity != this ) { >- listOfRelatedEntities.add( rel.destinationEntity ); >- } >- >- } >- return listOfRelatedEntities; >- } >- >- >- >- /** >- * Ignores this entity in the layout >- * @param ignore Should this entity be ignored >- */ >- public void ignoreInLayout( boolean ignore ) { >- this.ignoreInLayout = ignore; >- } >- >- public Object getRealObject() { >- return realObject; >- } >- >- public boolean hasPreferredLocation() { >- return this.ignoreInLayout; >- } >- >- /** >- * Gets the x position of this SimpleNode. >- */ >- public double getX() { >- return x; >- } >- >- /** >- * Gets the y position of this SimpleNode. >- */ >- public double getY() { >- return y; >- } >- >- /** >- * Get the size of this node >- */ >- public double getWidth() { >- return width; >- } >- >- /** >- * Get the size of this node >- */ >- public double getHeight() { >- return height; >- } >- >- public void setSizeInLayout(double width, double height) { >- if (!ignoreInLayout) { >- this.width = width; >- this.height = height; >- } >- } >- >- public void setLocation( double x, double y ) { >- this.x = x; >- this.y = y; >- } >- >- public void setLocationInLayout(double x, double y) { >- if (!ignoreInLayout) { >- this.x = x; >- this.y = y; >- } >- } >- >- /** >- * An algorithm may require a place to store information. Use this structure for that purpose. >- */ >- public void setAttributeInLayout (Object attribute, Object value) { >- attributes.put (attribute, value); >- } >- >- /** >- * An algorithm may require a place to store information. Use this structure for that purpose. >- */ >- public Object getAttributeInLayout (Object attribute) { >- return attributes.get (attribute); >- } >- >- public boolean equals(Object object) { >- boolean result = false; >- if (object instanceof SimpleNode) { >- SimpleNode node = (SimpleNode)object; >- result = realObject.equals (node.getRealObject()); >- } >- return result; >- } >- >- public int hashCode() { >- return realObject.hashCode(); >- } >- >- // all objects are equal >- public int compareTo(Object arg0) { >- return 0; >- } >- >- public String toString() { >- return realObject.toString(); >- } >- >- public void setSelected() { >- this.colour = NODE_SELECTED_COLOR; >- this.borderColor = BORDER_SELECTED_COLOR; >- this.borderWidth = BORDER_STROKE_SELECTED; >- } >- >- public void setUnSelected() { >- this.colour = NODE_NORMAL_COLOR; >- this.borderColor = BORDER_NORMAL_COLOR; >- this.borderWidth = BORDER_NORMAL_STROKE; >- } >- >- public void setAdjacent() { >- this.colour = NODE_ADJACENT_COLOR; >- this.borderColor = BORDER_ADJACENT_COLOR; >- this.borderWidth = BORDER_STROKE_SELECTED; >- } >- >- public Object getColor() { >- return this.colour; >- } >- >- public int getBorderWidth() { >- return borderWidth; >- } >- >- public Object getBorderColor() { >- return borderColor; >- } >- >- /* (non-Javadoc) >- * @see ca.uvic.cs.chisel.layouts.LayoutEntity#getInternalEntity() >- */ >- public Object getLayoutInformation() { >- return internalNode; >- } >- >- /* (non-Javadoc) >- * @see ca.uvic.cs.chisel.layouts.LayoutEntity#setInternalEntity(java.lang.Object) >- */ >- public void setLayoutInformation(Object internalEntity) { >- this.internalNode = internalEntity; >- } >- >- /** >- * Populate the specified layout constraint >- */ >- public void populateLayoutConstraint(LayoutConstraint constraint) { >- if ( constraint instanceof LabelLayoutConstraint ) { >- LabelLayoutConstraint labelConstraint = (LabelLayoutConstraint) constraint; >- labelConstraint.label = realObject.toString(); >- labelConstraint.pointSize = 18; >- } >- else if ( constraint instanceof BasicEntityConstraint ) { >- // noop >- } >- else if ( constraint instanceof EntityPriorityConstraint ) { >- EntityPriorityConstraint priorityConstraint = (EntityPriorityConstraint) constraint; >- priorityConstraint.priority = Math.random() * 10 + 1; >- } >- } >- >- public double getHeightInLayout() { >- return this.height; >- } >- >- public double getWidthInLayout() { >- return this.width; >- } >- >- public double getXInLayout() { >- return this.x; >- } >- >- public double getYInLayout() { >- return this.y; >- } >- >- public Object getGraphData() { >- return null; >- } >- >- public void setGraphData(Object o) { >- >- } >- >-} >Index: src/org/eclipse/zest/layouts/exampleStructures/SimpleRelationship.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/exampleStructures/SimpleRelationship.java >diff -N src/org/eclipse/zest/layouts/exampleStructures/SimpleRelationship.java >--- src/org/eclipse/zest/layouts/exampleStructures/SimpleRelationship.java 12 Jan 2008 01:44:24 -0000 1.7 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,274 +0,0 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts.exampleStructures; >- >-import java.util.HashMap; >-import java.util.Map; >- >-import org.eclipse.zest.layouts.LayoutBendPoint; >-import org.eclipse.zest.layouts.LayoutEntity; >-import org.eclipse.zest.layouts.LayoutRelationship; >-import org.eclipse.zest.layouts.constraints.BasicEdgeConstraints; >-import org.eclipse.zest.layouts.constraints.LabelLayoutConstraint; >-import org.eclipse.zest.layouts.constraints.LayoutConstraint; >-import org.eclipse.zest.layouts.dataStructures.BendPoint; >- >- >-/** >- * The SimpleRelation class describes the relationship between >- * two objects: source and destination. Each relationship >- * has a weight and direction associated with it. >- * Note: The source object is at the beginning of the relationship. >- * Note: The destination object is at the end of the relationship. >- * >- * @version 2.0 >- * @author Casey Best (version 1.0 by Jingwei Wu) >- * @author Chris Bennett >- */ >-public class SimpleRelationship implements LayoutRelationship { >- >- private static int DEFAULT_REL_LINE_WIDTH = 1; >- private static int DEFAULT_REL_LINE_WIDTH_SELECTED = DEFAULT_REL_LINE_WIDTH + 2; >- private static Object DEFAULT_RELATIONSHIP_COLOR; >- private static Object DEFAULT_RELATIONSHIP_HIGHLIGHT_COLOR; >- >- /** The line width for this relationship. */ >- private int lineWidth = DEFAULT_REL_LINE_WIDTH; >- >- /** The color for this relationship. */ >- private Object color = DEFAULT_RELATIONSHIP_COLOR; >- >- /** >- * A list of layout dependent attributes >- */ >- private Map attributes; >- >- /** >- * The sourceEntity of this SimpleRelation. >- */ >- protected LayoutEntity sourceEntity; >- >- /** >- * The object of this SimpleRelation. >- */ >- protected LayoutEntity destinationEntity; >- >- /** >- * If directional, algorithms must note the direction of the relationship. >- * If not directional, algorithms are to ignore which direction the relationship is going. >- * Switching the source and destination should make no difference. >- */ >- protected boolean bidirectional; >- >- /** >- * The weight given to this relation. >- */ >- private double weight; >- >- private Object internalRelationship; >- >- private LayoutBendPoint[] bendPoints; >- >- private String label; >- >- /** >- * Constructor. >- * @param sourceEntity The sourceEntity of this SimpleRelation. >- * @param destinationEntity The object of this SimpleRelation. >- * @param bidirectional Determines if the <code>sourceEntity</code> and >- * <code>destinationEntity</code> are equal(exchangeable). >- * @throws java.lang.NullPointerException If either <code>sourceEntity >- * </code> or <code>destinationEntity</code> is <code>null</code>. >- */ >- public SimpleRelationship(LayoutEntity sourceEntity, LayoutEntity destinationEntity, boolean bidirectional) { >- this (sourceEntity, destinationEntity, bidirectional, 1); >- } >- >- /** >- * Constructor. >- * @param sourceEntity The sourceEntity of this SimpleRelation. >- * @param destinationEntity The destinationEntity of this SimpleRelation. >- * @param exchangeable Determines if the <code>sourceEntity</code> and >- * <code>destinationEntity</code> are equal(exchangeable). >- * @throws java.lang.NullPointerException If either <code>sourceEntity >- * </code> or <code>destinationEntity</code> is <code>null</code>. >- */ >- public SimpleRelationship(LayoutEntity sourceEntity, LayoutEntity destinationEntity, boolean bidirectional, double weight) { >- this.destinationEntity = destinationEntity; >- this.sourceEntity = sourceEntity; >- this.bidirectional = bidirectional; >- this.weight = weight; >- this.attributes = new HashMap(); >- this.lineWidth = DEFAULT_REL_LINE_WIDTH; >- this.color = DEFAULT_RELATIONSHIP_COLOR; >- } >- >- /** >- * Gets the sourceEntity of this SimpleRelation whether the relation is >- * exchangeable or not. >- * @return The sourceEntity. >- */ >- public LayoutEntity getSourceInLayout() { >- return sourceEntity; >- } >- >- /** >- * Gets the destinationEntity of this SimpleRelation whether the relation is >- * exchangeable or not. >- * @return The destinationEntity of this SimpleRelation. >- */ >- public LayoutEntity getDestinationInLayout() { >- return destinationEntity; >- } >- >- /** >- * If bidirectional, the direction of the relationship doesn't matter. Switching the source and destination should make no difference. >- * If not bidirectional, layout algorithms need to take into account the direction of the relationship. The direction is based on the >- * source and destination entities. >- */ >- public boolean isBidirectionalInLayout() { >- return bidirectional; >- } >- >- public void setWeightInLayout(double weight) { >- this.weight = weight; >- } >- >- public double getWeightInLayout() { >- return weight; >- } >- >- /** >- * An algorithm may require a place to store information. Use this structure for that purpose. >- */ >- public void setAttributeInLayout (String attribute, Object value) { >- attributes.put (attribute, value); >- } >- >- /** >- * An algorithm may require a place to store information. Use this structure for that purpose. >- */ >- public Object getAttributeInLayout (String attribute) { >- return attributes.get (attribute); >- } >- >- public String toString() { >- String arrow = (isBidirectionalInLayout() ? " <-> " : " -> "); >- return "(" + sourceEntity + arrow + destinationEntity + ")"; >- } >- >- public int getLineWidth() { >- return this.lineWidth; >- } >- >- public void setLineWidth( int lineWidth ) { >- this.lineWidth = lineWidth; >- } >- >- public void resetLineWidth() { >- this.lineWidth = DEFAULT_REL_LINE_WIDTH; >- } >- >- public static void setDefaultSize(int i) { >- DEFAULT_REL_LINE_WIDTH = i; >- DEFAULT_REL_LINE_WIDTH_SELECTED = DEFAULT_REL_LINE_WIDTH + 2; >- } >- >- public void setSelected() { >- this.color = DEFAULT_RELATIONSHIP_HIGHLIGHT_COLOR; >- this.lineWidth = DEFAULT_REL_LINE_WIDTH_SELECTED; >- } >- >- public void setUnSelected() { >- this.color = DEFAULT_RELATIONSHIP_COLOR; >- this.lineWidth = DEFAULT_REL_LINE_WIDTH; >- } >- >- public Object getColor() { >- return color; >- } >- >- public void setColor(Object c) { >- this.color = c; >- } >- >- public static void setDefaultColor(Object c) { >- DEFAULT_RELATIONSHIP_COLOR = c; >- } >- >- public static void setDefaultHighlightColor(Object c) { >- DEFAULT_RELATIONSHIP_HIGHLIGHT_COLOR = c; >- } >- >- /* (non-Javadoc) >- * @see ca.uvic.cs.chisel.layouts.LayoutRelationship#getInternalRelationship() >- */ >- public Object getLayoutInformation() { >- return internalRelationship; >- } >- >- /* (non-Javadoc) >- * @see ca.uvic.cs.chisel.layouts.LayoutRelationship#setInternalRelationship(java.lang.Object) >- */ >- public void setLayoutInformation(Object layoutInformation) { >- this.internalRelationship = layoutInformation; >- } >- >- >- public void setBendPoints(LayoutBendPoint[] bendPoints) { >- this.bendPoints = bendPoints; >- } >- >- public LayoutBendPoint[] getBendPoints() { >- return this.bendPoints; >- } >- >- public void clearBendPoints() { >- this.bendPoints = new BendPoint[0]; >- } >- >- >- public void setDestinationInLayout(LayoutEntity destination) { >- this.destinationEntity = destination; >- } >- >- /** >- * Set the label for this edge (available in the label layout constraint). >- */ >- public void setLabel(String label) { >- this.label = label; >- } >- >- /** >- * Populate the specified layout constraint >- */ >- public void populateLayoutConstraint(LayoutConstraint constraint) { >- if ( constraint instanceof LabelLayoutConstraint ) { >- LabelLayoutConstraint labelConstraint = (LabelLayoutConstraint) constraint; >- labelConstraint.label = this.label; >- labelConstraint.pointSize = 18; >- } >- else if ( constraint instanceof BasicEdgeConstraints ) { >- // noop >- >- } >- } >- >- public Object getGraphData() { >- return null; >- } >- >- public void setGraphData(Object o) { >- >- } >- >- >-} >Index: src/org/eclipse/zest/layouts/exampleUses/SimpleSWTExample.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/exampleUses/SimpleSWTExample.java >diff -N src/org/eclipse/zest/layouts/exampleUses/SimpleSWTExample.java >--- src/org/eclipse/zest/layouts/exampleUses/SimpleSWTExample.java 12 Sep 2007 20:44:38 -0000 1.14 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,785 +0,0 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts.exampleUses; >- >-import java.util.ArrayList; >-import java.util.Date; >-import java.util.Iterator; >-import java.util.List; >- >-import org.eclipse.core.runtime.IProgressMonitor; >-import org.eclipse.jface.dialogs.ProgressMonitorDialog; >-import org.eclipse.zest.layouts.InvalidLayoutConfiguration; >-import org.eclipse.zest.layouts.LayoutAlgorithm; >-import org.eclipse.zest.layouts.LayoutBendPoint; >-import org.eclipse.zest.layouts.LayoutEntity; >-import org.eclipse.zest.layouts.LayoutRelationship; >-import org.eclipse.zest.layouts.LayoutStyles; >-import org.eclipse.zest.layouts.algorithms.GridLayoutAlgorithm; >-import org.eclipse.zest.layouts.algorithms.HorizontalLayoutAlgorithm; >-import org.eclipse.zest.layouts.algorithms.HorizontalTreeLayoutAlgorithm; >-import org.eclipse.zest.layouts.algorithms.RadialLayoutAlgorithm; >-import org.eclipse.zest.layouts.algorithms.SpringLayoutAlgorithm; >-import org.eclipse.zest.layouts.algorithms.TreeLayoutAlgorithm; >-import org.eclipse.zest.layouts.algorithms.VerticalLayoutAlgorithm; >-import org.eclipse.zest.layouts.exampleStructures.SimpleNode; >-import org.eclipse.zest.layouts.exampleStructures.SimpleRelationship; >-import org.eclipse.zest.layouts.progress.ProgressEvent; >-import org.eclipse.zest.layouts.progress.ProgressListener; >-import org.eclipse.swt.SWT; >-import org.eclipse.swt.SWTError; >-import org.eclipse.swt.events.ControlEvent; >-import org.eclipse.swt.events.ControlListener; >-import org.eclipse.swt.events.DisposeEvent; >-import org.eclipse.swt.events.DisposeListener; >-import org.eclipse.swt.events.MouseEvent; >-import org.eclipse.swt.events.MouseListener; >-import org.eclipse.swt.events.MouseMoveListener; >-import org.eclipse.swt.events.PaintEvent; >-import org.eclipse.swt.events.PaintListener; >-import org.eclipse.swt.events.SelectionEvent; >-import org.eclipse.swt.events.SelectionListener; >-import org.eclipse.swt.graphics.Color; >-import org.eclipse.swt.graphics.GC; >-import org.eclipse.swt.graphics.Image; >-import org.eclipse.swt.graphics.Point; >-import org.eclipse.swt.graphics.Rectangle; >-import org.eclipse.swt.layout.FillLayout; >-import org.eclipse.swt.layout.GridData; >-import org.eclipse.swt.layout.GridLayout; >-import org.eclipse.swt.widgets.Canvas; >-import org.eclipse.swt.widgets.Composite; >-import org.eclipse.swt.widgets.Display; >-import org.eclipse.swt.widgets.Label; >-import org.eclipse.swt.widgets.Shell; >-import org.eclipse.swt.widgets.ToolBar; >-import org.eclipse.swt.widgets.ToolItem; >- >-/** >- * @author Rob Lintern >- * @author Ian Bull >- * A simple example of using layout algorithms with a SWT application. >- */ >-public class SimpleSWTExample { >- >- private static final Color BLACK = new Color(Display.getDefault(), 0, 0, 0); >- private static final Color NODE_NORMAL_COLOR = new Color(Display.getDefault(), 225, 225, 255); >- private static final Color NODE_SELECTED_COLOR = new Color(Display.getDefault(), 255, 125, 125); >- private static final Color NODE_ADJACENT_COLOR = new Color(Display.getDefault(), 255, 200, 125); >- private static final Color BORDER_NORMAL_COLOR = new Color(Display.getDefault(), 0, 0, 0); >- private static final Color BORDER_SELECTED_COLOR = new Color(Display.getDefault(), 255, 0, 0); >- private static final Color BORDER_ADJACENT_COLOR = new Color(Display.getDefault(), 255, 128, 0); >- private static final Color RELATIONSHIP_COLOR = new Color(Display.getDefault(), 192, 192, 225); >- private static final Color RELATIONSHIP_HIGHLIGHT_COLOR = new Color(Display.getDefault(), 255, 200, 125); >- >- private static final String[] NAMES = new String[] { "Peggy", "Rob", "Ian", "Chris", "Simon", "Wendy", "Steven", "Kim", "Neil", "Dave", "John", "Suzanne", "Jody", "Casey", "Bjorn", "Peter", "Erin", "Lisa", "Jennie", "Liz", "Bert", "Ryan", "Nick", "Amy", "Lee", "Me", "You", "Max", "NCI", "OWL", >- "Ed", "Jamie", "Protege", "Matt", "Bryan", "Pete", "Sam", "Bob", "Katie", "Bill", "Josh", "Davor", "Ken", "Jacob", "Norm", "Jim", "Maya", "Jill", "Kit", "Jo", "Joe", "Andrew", "Charles", "Pat", "Patrick", "Jeremy", "Mike", "Michael", "Patricia", "Marg", "Terry", "Emily", "Ben", "Holly", >- "Joanna", "Joanne", "Evan", "Tom", "Dan", "Eric", "Corey", "Meghan", "Kevin", "Nina", "Ron", "Daniel", "David", "Jeff", "Nathan", "Amanda", "Phil", "Tricia", "Steph", "Stewart", "Stuart", "Bull", "Lintern", "Callendar", "Thompson", "Rigby", "Adam", "Judith", "Cynthia", "Sarah", "Sara", >- "Roger", "Andy", "Kris", "Mark", "Shane", "Spence", "Ivy", "Ivanna", "Julie", "Justin", "Emile", "Toby", "Robin", "Rich", "Kathy", "Cathy", "Nicky", "Ricky", "Danny", "Anne", "Ann", "Jen", "Robert", "Calvin", "Alvin", "Scott", "Kumar" }; >- >- //private static final boolean RENDER_HIGH_QUALITY = true; >- >- private static final int INITIAL_PANEL_WIDTH = 800; >- private static final int INITIAL_PANEL_HEIGHT = 600; >- private static final double INITIAL_NODE_WIDTH = 20; >- private static final double INITIAL_NODE_HEIGHT = 15; >- >- protected static ArrayList algorithms = new ArrayList(); >- { >- algorithms.add(new SpringLayoutAlgorithm(LayoutStyles.NO_LAYOUT_NODE_RESIZING)); >- algorithms.add(new TreeLayoutAlgorithm(LayoutStyles.NONE)); >- algorithms.add(new HorizontalTreeLayoutAlgorithm(LayoutStyles.NONE)); >- algorithms.add(new RadialLayoutAlgorithm(LayoutStyles.NONE)); >- algorithms.add(new GridLayoutAlgorithm(LayoutStyles.NONE)); >- algorithms.add(new HorizontalLayoutAlgorithm(LayoutStyles.NONE)); >- algorithms.add(new VerticalLayoutAlgorithm(LayoutStyles.NONE)); >- } >- >- protected static ArrayList algorithmNames = new ArrayList(); >- { >- algorithmNames.add("Spring"); >- algorithmNames.add("Fade"); >- algorithmNames.add("Tree - V"); >- algorithmNames.add("Tree - H"); >- algorithmNames.add("Radial"); >- algorithmNames.add("Grid"); >- algorithmNames.add("Horizontal"); >- algorithmNames.add("Vertical"); >- } >- >- protected static ArrayList algorithmAnimates = new ArrayList(); >- { >- algorithmAnimates.add(Boolean.TRUE); >- algorithmAnimates.add(Boolean.TRUE); >- algorithmAnimates.add(Boolean.FALSE); >- algorithmAnimates.add(Boolean.FALSE); >- algorithmAnimates.add(Boolean.FALSE); >- algorithmAnimates.add(Boolean.FALSE); >- algorithmAnimates.add(Boolean.FALSE); >- algorithmAnimates.add(Boolean.FALSE); >- } >- >- //private long updateGUICount = 0; >- private boolean animate = true; >- private static boolean continuous = false; >- private static boolean asynchronously = false; >- >- private Shell mainShell; >- private Composite mainComposite; >- private List entities; >- private List relationships; >- >- private ToolBar toolBar; >- private Label lblProgress; >- >- private LayoutAlgorithm currentLayoutAlgorithm; >- protected SimpleNode selectedEntity; >- protected SimpleNode hoverEntity; >- >- protected Point mouseDownPoint; >- protected Point selectedEntityPositionAtMouseDown; >- private long idCount = 0; >- >- public SimpleSWTExample(Display display) { >- mainShell = new Shell(display); >- mainShell.addControlListener(new ControlListener() { >- public void controlMoved(ControlEvent e) { >- } >- >- public void controlResized(ControlEvent e) { >- mainShell.layout(true); >- } >- }); >- GridLayout gridLayout = new GridLayout(1, true); >- mainShell.setLayout(gridLayout); >- GridData toolbarGridData = new GridData(GridData.HORIZONTAL_ALIGN_CENTER, GridData.VERTICAL_ALIGN_BEGINNING, true, true); >- toolBar = new ToolBar(mainShell, SWT.HORIZONTAL); >- toolBar.setLayoutData(toolbarGridData); >- toolBar.setLayout(new FillLayout(SWT.HORIZONTAL)); >- >- GridData progressGridData = new GridData(GridData.HORIZONTAL_ALIGN_CENTER, GridData.VERTICAL_ALIGN_END, true, false); >- progressGridData.widthHint = 300; >- lblProgress = new Label(mainShell, SWT.NONE); >- lblProgress.setLayoutData(progressGridData); >- lblProgress.setText("Progress: "); >- >- for (int i = 0; i < algorithms.size(); i++) { >- final LayoutAlgorithm algorithm = (LayoutAlgorithm) algorithms.get(i); >- String algorithmName = (String) algorithmNames.get(i); >- final boolean algorithmAnimate = ((Boolean) algorithmAnimates.get(i)).booleanValue(); >- ToolItem algorithmButton = new ToolItem(toolBar, SWT.PUSH); >- algorithmButton.setText(algorithmName); >- >- new ToolItem(toolBar, SWT.SEPARATOR); >- >- algorithmButton.addSelectionListener(new SelectionListener() { >- public void widgetSelected(org.eclipse.swt.events.SelectionEvent e) { >- currentLayoutAlgorithm = algorithm; >- algorithm.setEntityAspectRatio((double) mainComposite.getClientArea().width / (double) mainComposite.getClientArea().height); >- animate = algorithmAnimate; >- performLayout(false); >- } >- >- public void widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent e) { >- } >- }); >- } >- >- ToolItem redrawButton = new ToolItem(toolBar, SWT.PUSH); >- redrawButton.setText("Redraw"); >- redrawButton.addSelectionListener(new SelectionListener() { >- public void widgetSelected(SelectionEvent e) { >- mainComposite.redraw(); >- } >- >- public void widgetDefaultSelected(SelectionEvent e) { >- } >- }); >- >- ToolItem stopButton = new ToolItem(toolBar, SWT.PUSH); >- stopButton.setText("Stop"); >- stopButton.addSelectionListener(new SelectionListener() { >- public void widgetSelected(SelectionEvent e) { >- currentLayoutAlgorithm.stop(); >- } >- >- public void widgetDefaultSelected(SelectionEvent e) { >- } >- }); >- >- ToolItem continuousButton = new ToolItem(toolBar, SWT.CHECK); >- continuousButton.setText("Continuous"); >- continuousButton.addSelectionListener(new SelectionListener() { >- public void widgetSelected(SelectionEvent e) { >- setContinuous(); >- } >- >- public void widgetDefaultSelected(SelectionEvent e) { >- } >- }); >- >- ToolItem asynchronousButton = new ToolItem(toolBar, SWT.CHECK); >- asynchronousButton.setText("Asynchronous"); >- asynchronousButton.addSelectionListener(new SelectionListener() { >- public void widgetSelected(SelectionEvent e) { >- setAsynchronously(); >- } >- >- public void widgetDefaultSelected(SelectionEvent e) { >- } >- }); >- >- createMainPanel(); >- SimpleNode.setNodeColors(NODE_NORMAL_COLOR, BORDER_NORMAL_COLOR, NODE_SELECTED_COLOR, NODE_ADJACENT_COLOR, BORDER_SELECTED_COLOR, BORDER_ADJACENT_COLOR); >- SimpleRelationship.setDefaultColor(RELATIONSHIP_COLOR); >- SimpleRelationship.setDefaultHighlightColor(RELATIONSHIP_HIGHLIGHT_COLOR); >- createTreeGraph(4, 3, false); >- mainShell.pack(); >- //mainShell.setSize(INITIAL_PANEL_WIDTH + 100, INITIAL_PANEL_HEIGHT + 200); >- } >- >- public void setAsynchronously() { >- if (asynchronously) { >- asynchronously = false; >- } else { >- asynchronously = true; >- } >- >- } >- >- public void setContinuous() { >- if (continuous) { >- continuous = false; >- } else { >- continuous = true; >- } >- } >- >- IProgressMonitor progressMonitor = null; >- ProgressMonitorDialog pmd = null; >- boolean GUI_UPDATING = false; >- >- private void performLayout(boolean placeRandomly) { >- >- if (!continuous) { >- } >- >- if (currentLayoutAlgorithm.isRunning()) { >- throw new RuntimeException("Layout is already running"); >- } >- if (placeRandomly) { >- placeRandomly(); >- } >- ProgressListener progressListener = new ProgressListener() { >- >- int lastStep = 0; >- >- class progressSync implements Runnable { >- >- public static final int PROGRESS_UPDATED = 1; >- public static final int PROGRESS_STARTED = 2; >- public static final int PROGRESS_ENDED = 3; >- public static final int UPDATE_GUI = 4; >- >- private int progressState = -1; >- private ProgressEvent e; >- >- public progressSync(int progressState, final ProgressEvent e) { >- this.progressState = progressState; >- this.e = e; >- >- } >- >- public void run() { >- >- switch (progressState) { >- case PROGRESS_STARTED: >- if (!continuous) { >- pmd = new ProgressMonitorDialog(getShell()); >- progressMonitor = pmd.getProgressMonitor(); >- pmd.open(); >- progressMonitor.beginTask("Layout Running...", e.getTotalNumberOfSteps()); >- } >- break; >- >- case PROGRESS_UPDATED: >- if (!continuous) { >- progressMonitor.worked(e.getStepsCompleted() - lastStep); >- lastStep = e.getStepsCompleted(); >- } >- break; >- >- case PROGRESS_ENDED: >- if (!continuous) { >- progressMonitor.done(); >- pmd.close(); >- } >- updateGUI(); >- mainShell.redraw(); >- break; >- case UPDATE_GUI: >- updateGUI(); >- GUI_UPDATING = false; >- break; >- } >- mainComposite.redraw(); >- >- } >- >- } >- >- public void progressUpdated(final ProgressEvent e) { >- if (asynchronously) { >- if (!mainComposite.isDisposed()) { >- Display.getDefault().asyncExec(new progressSync(progressSync.PROGRESS_UPDATED, e)); >- if (!GUI_UPDATING) { >- GUI_UPDATING = true; >- Display.getDefault().asyncExec(new progressSync(progressSync.UPDATE_GUI, e)); >- } >- } >- } else { >- if (!mainComposite.isDisposed()) { >- new progressSync(progressSync.PROGRESS_UPDATED, e).run(); >- } >- } >- >- } >- >- public void progressStarted(ProgressEvent e) { >- if (asynchronously) { >- if (!mainComposite.isDisposed()) { >- Display.getDefault().asyncExec(new progressSync(progressSync.PROGRESS_STARTED, e)); >- } >- } else { >- if (!mainComposite.isDisposed()) { >- new progressSync(progressSync.PROGRESS_STARTED, e).run(); >- } >- } >- >- } >- >- public void progressEnded(ProgressEvent e) { >- if (asynchronously) { >- if (!mainComposite.isDisposed()) { >- Display.getDefault().asyncExec(new progressSync(progressSync.PROGRESS_ENDED, e)); >- } >- } else { >- if (!mainComposite.isDisposed()) { >- new progressSync(progressSync.PROGRESS_ENDED, e).run(); >- } >- } >- currentLayoutAlgorithm.removeProgressListener(this); >- Display.getDefault().asyncExec(new progressSync(progressSync.PROGRESS_UPDATED, e)); >- } >- }; >- currentLayoutAlgorithm.addProgressListener(progressListener); >- >- try { >- LayoutEntity[] layoutEntities = new LayoutEntity[entities.size()]; >- entities.toArray(layoutEntities); >- LayoutRelationship[] layoutRelationships = new LayoutRelationship[relationships.size()]; >- relationships.toArray(layoutRelationships); >- currentLayoutAlgorithm.applyLayout(layoutEntities, layoutRelationships, 0, 0, mainComposite.getClientArea().width - 30, mainComposite.getClientArea().height - 17, asynchronously, continuous); >- if (!animate) { >- updateGUI(); >- } >- } catch (InvalidLayoutConfiguration e) { >- e.printStackTrace(); >- } >- } >- >- private Shell getShell() { >- return mainShell; >- } >- >- private void createMainPanel() { >- mainComposite = new Canvas(mainShell, SWT.NO_BACKGROUND); >- GridData mainGridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL, GridData.VERTICAL_ALIGN_FILL, true, true); >- mainGridData.widthHint = INITIAL_PANEL_WIDTH; >- mainGridData.heightHint = INITIAL_PANEL_HEIGHT; >- mainComposite.setLayoutData(mainGridData); >- mainComposite.addPaintListener(new GraphPaintListener()); >- >- mainComposite.setBackground(new Color(Display.getCurrent(), 255, 255, 255)); >- mainComposite.setLayout(null); >- >- mainComposite.addMouseMoveListener(new MouseMoveListener() { >- >- public void mouseMove(MouseEvent e) { >- >- if (selectedEntity == null) { >- // Nothing selected, lets use a mouse hover >- SimpleNode oldEntity = hoverEntity; >- hoverEntity = null; >- >- for (Iterator iter = entities.iterator(); iter.hasNext() && selectedEntity == null;) { >- SimpleNode entity = (SimpleNode) iter.next(); >- double x = entity.getX(); >- double y = entity.getY(); >- double w = entity.getWidth(); >- double h = entity.getHeight(); >- Rectangle rect = new Rectangle((int) x, (int) y, (int) w, (int) h); >- if (rect.contains(e.x, e.y)) { >- hoverEntity = entity; >- hoverEntity.ignoreInLayout(true); >- hoverEntity.setSelected(); >- break; >- } >- } >- if (oldEntity != null && oldEntity != hoverEntity) { >- oldEntity.ignoreInLayout(false); >- oldEntity.setUnSelected(); >- } >- } >- >- } >- >- }); >- mainComposite.addMouseListener(new MouseListener() { >- >- public void mouseDoubleClick(MouseEvent e) { >- } >- >- public void mouseDown(MouseEvent e) { >- selectedEntity = null; >- hoverEntity = null; >- for (Iterator iter = entities.iterator(); iter.hasNext() && selectedEntity == null;) { >- SimpleNode entity = (SimpleNode) iter.next(); >- double x = entity.getX(); >- double y = entity.getY(); >- double w = entity.getWidth(); >- double h = entity.getHeight(); >- Rectangle rect = new Rectangle((int) x, (int) y, (int) w, (int) h); >- if (rect.contains(e.x, e.y)) { >- selectedEntity = entity; >- } >- } >- if (selectedEntity != null) { >- mouseDownPoint = new Point(e.x, e.y); >- selectedEntityPositionAtMouseDown = new Point((int) selectedEntity.getX(), (int) selectedEntity.getY()); >- selectedEntity.ignoreInLayout(true); >- selectedEntity.setSelected(); >- } else { >- mouseDownPoint = null; >- selectedEntityPositionAtMouseDown = null; >- } >- } >- >- public void mouseUp(MouseEvent e) { >- if (selectedEntity != null) { >- selectedEntity.ignoreInLayout(false); >- selectedEntity.setUnSelected(); >- List relatedNodes = selectedEntity.getRelatedEntities(); >- for (Iterator iter = relatedNodes.iterator(); iter.hasNext();) { >- SimpleNode element = (SimpleNode) iter.next(); >- element.setUnSelected(); >- } >- SimpleRelationship[] rels = selectedEntity.getRelationships(); >- for (int i = 0; i < rels.length; i++) { >- rels[i].resetLineWidth(); >- } >- } >- selectedEntity = null; >- mouseDownPoint = null; >- selectedEntityPositionAtMouseDown = null; >- } >- }); >- >- // stops the algorithm when the window is closed >- mainComposite.addDisposeListener(new DisposeListener() { >- public void widgetDisposed(DisposeEvent e) { >- if (currentLayoutAlgorithm != null) { >- currentLayoutAlgorithm.stop(); >- } >- } >- }); >- >- mainComposite.addMouseMoveListener(new MouseMoveListener() { >- >- public void mouseMove(MouseEvent e) { >- if (selectedEntity != null && mouseDownPoint != null) { >- double dx = e.x - mouseDownPoint.x; >- double dy = e.y - mouseDownPoint.y; >- >- selectedEntity.setLocation(selectedEntityPositionAtMouseDown.x + dx, selectedEntityPositionAtMouseDown.y + dy); >- mainComposite.redraw(); >- } >- } >- }); >- } >- >- static int lastUpdateCall = 0; >- >- /** >- * >- * @param maxLevels Max number of levels wanted in tree >- * @param maxChildren Max number of children for each node in the tree >- * @param random Whether or not to pick random number of levels (from 1 to maxLevels) and >- * random number of children (from 1 to maxChildren) >- */ >- private void createTreeGraph(int maxLevels, int maxChildren, boolean random) { >- entities = new ArrayList(); >- relationships = new ArrayList(); >- >- // ccallendar - testing out having 2 roots >- SimpleNode root = createSimpleNode(getNextID()); >- entities.add(root); >- SimpleNode root2 = createSimpleNode(getNextID()); >- entities.add(root2); >- // end >- >- SimpleNode currentParent = createSimpleNode(getNextID()); >- entities.add(currentParent); >- >- // ccallendar - adding relationships from the parent to the 2 roots >- SimpleRelationship rel = new SimpleRelationship(root, currentParent, false); >- root.addRelationship(rel); >- currentParent.addRelationship(rel); >- relationships.add(rel); >- rel = new SimpleRelationship(root2, currentParent, false); >- root2.addRelationship(rel); >- currentParent.addRelationship(rel); >- relationships.add(rel); >- // end >- >- int levels = random ? (int) (Math.random() * maxLevels + 1) : maxLevels; >- createTreeGraphRecursive(currentParent, maxChildren, levels, 1, random); >- } >- >- private void createTreeGraphRecursive(SimpleNode currentParentNode, int maxChildren, int maxLevel, int level, boolean random) { >- if (level > maxLevel) { >- return; >- } >- >- int numChildren = random ? (int) (Math.random() * maxChildren + 1) : maxChildren; >- for (int child = 0; child < numChildren; child++) { >- SimpleNode childNode = createSimpleNode(getNextID()); >- entities.add(childNode); >- SimpleRelationship rel = new SimpleRelationship(currentParentNode, childNode, false); >- childNode.addRelationship(rel); >- currentParentNode.addRelationship(rel); >- relationships.add(rel); >- SimpleRelationship.setDefaultSize(2); >- createTreeGraphRecursive(childNode, maxChildren, maxLevel, level + 1, random); >- } >- } >- >- private int repeats = 0; >- >- /** >- * Gets the next name from the names list. >- * Once all the names have been used up the names are >- * repeated with a '1' after the name. >- * @return String name >- */ >- private String getNextID() { >- if (idCount >= NAMES.length) { >- idCount = 0; >- repeats++; >- } >- String id = NAMES[(int) idCount]; >- if (repeats > 0) { >- id += "_" + repeats; >- } >- idCount++; >- return id; >- } >- >- /** Places nodes randomly on the screen **/ >- private void placeRandomly() { >- for (Iterator iter = entities.iterator(); iter.hasNext();) { >- SimpleNode simpleNode = (SimpleNode) iter.next(); >- double x = Math.random() * INITIAL_PANEL_WIDTH - INITIAL_NODE_WIDTH; >- double y = Math.random() * INITIAL_PANEL_HEIGHT - INITIAL_NODE_HEIGHT; >- simpleNode.setLocationInLayout(x, y); >- } >- } >- >- /** >- * Creates a SimpleNode >- * @param name >- * @return SimpleNode >- */ >- private SimpleNode createSimpleNode(String name) { >- SimpleNode simpleNode = new SimpleNode(name); >- int w = name.length() * 8; // an initial approximation of the width >- simpleNode.setSizeInLayout(Math.max(w, INITIAL_NODE_WIDTH), INITIAL_NODE_HEIGHT); >- return simpleNode; >- } >- >- private void updateGUI() { >- if (!mainComposite.isDisposed()) { >- mainComposite.redraw(); >- //mainComposite.update(); >- } >- } >- >- static Display display = null; >- >- public static void main(String[] args) { >- display = Display.getDefault(); >- SimpleSWTExample simpleSWTExample = new SimpleSWTExample(display); >- Shell shell = simpleSWTExample.getShell(); >- //shell.pack(); >- shell.open(); >- while (!shell.isDisposed()) { >- if (!display.readAndDispatch()) { >- display.sleep(); >- } >- } >- display.dispose(); >- } >- >- /** >- * Implements a paint listener to display nodes and edges >- */ >- private class GraphPaintListener implements PaintListener { >- >- long lastPaint; >- >- public void paintControl(PaintEvent e) { >- Date date = new Date(); >- long currentTime = date.getTime(); >- if (currentTime - lastPaint < 40) { >- return; >- } else { >- lastPaint = currentTime; >- } >- if (Display.getDefault() == null || e.width == 0 || e.height == 0) { >- return; >- } >- long startTime = date.getTime(); >- >- // do a bit of our own double-buffering to stop flickering >- Image imageBuffer; >- >- try { >- imageBuffer = new Image(Display.getDefault(), e.width, e.height); >- } catch (SWTError noMoreHandles) { >- imageBuffer = null; >- noMoreHandles.printStackTrace(); >- return; >- } catch (IllegalArgumentException tooBig) { >- imageBuffer = null; >- tooBig.printStackTrace(); >- return; >- } >- >- GC gcBuffer = new GC(imageBuffer); >- >- // paint the relationships >- for (Iterator iter = relationships.iterator(); iter.hasNext();) { >- SimpleRelationship rel = (SimpleRelationship) iter.next(); >- SimpleNode src = (SimpleNode) rel.getSourceInLayout(); >- SimpleNode dest = (SimpleNode) rel.getDestinationInLayout(); >- >- // highlight the adjacent nodes if one of the nodes is selected >- if (src.equals(selectedEntity)) { >- dest.setAdjacent(); >- rel.setSelected(); >- } else if (dest.equals(selectedEntity)) { >- src.setAdjacent(); >- rel.setSelected(); >- } else { >- rel.setUnSelected(); >- } >- >- // Add bend points if required >- if ((rel).getBendPoints() != null && (rel).getBendPoints().length > 0) { >- src = drawBendPoints(rel, gcBuffer); // change source to last bendpoint >- } >- >- double srcX = src.getX() + src.getWidth() / 2.0; >- double srcY = src.getY() + src.getHeight() / 2.0; >- double destX = dest.getX() + dest.getWidth() / 2.0; >- double destY = dest.getY() + dest.getHeight() / 2.0; >- drawEdge(srcX, srcY, destX, destY, rel, gcBuffer); >- >- } >- >- // paint the nodes >- for (Iterator iter = entities.iterator(); iter.hasNext();) { >- SimpleNode entity = (SimpleNode) iter.next(); >- >- String name = entity.toString(); >- Point textSize = gcBuffer.stringExtent(name); >- int entityX = (int) entity.getX(); >- int entityY = (int) entity.getY(); >- //TODO: What about resize from the layout algorithm >- int entityWidth = Math.max((int) entity.getWidth(), textSize.x + 8); >- int entityHeight = Math.max((int) entity.getHeight(), textSize.y + 2); >- >- gcBuffer.setBackground((Color) entity.getColor()); >- gcBuffer.fillRoundRectangle(entityX, entityY, entityWidth, entityHeight, 8, 8); >- >- // position the text in the middle of the node >- int x = (int) (entityX + (entityWidth / 2.0)) - (textSize.x / 2); >- gcBuffer.setForeground(BLACK); >- gcBuffer.drawString(name, x, entityY); >- gcBuffer.setForeground((Color) entity.getBorderColor()); >- gcBuffer.setLineWidth(entity.getBorderWidth()); >- gcBuffer.drawRoundRectangle(entityX, entityY, entityWidth, entityHeight, 8, 8); >- } >- >- e.gc.drawImage(imageBuffer, 0, 0); >- imageBuffer.dispose(); >- gcBuffer.dispose(); >- >- long time = date.getTime() - startTime; >- if (time > 200) { >- } >- } >- >- /** >- * Draw an edge >- * @param gcBuffer >- * @param srcX >- * @param srcY >- * @param destX >- * @param destY >- * @param rel >- */ >- private void drawEdge(double srcX, double srcY, double destX, double destY, SimpleRelationship rel, GC gcBuffer) { >- gcBuffer.setForeground((Color) rel.getColor()); >- gcBuffer.setLineWidth(rel.getLineWidth()); >- gcBuffer.drawLine((int) srcX, (int) srcY, (int) destX, (int) destY); >- } >- >- /** >- * Draws a set of lines between bendpoints >- * TODO - This does not always draw outside the node. >- * @param relationship >- * @param bendNodes >- * @param bendEdges >- * @return the last bendpoint entity or null if there are no bendpoints >- */ >- private SimpleNode drawBendPoints(SimpleRelationship rel, GC gcBuffer) { >- final String DUMMY_TITLE = "dummy"; >- LayoutBendPoint[] bendPoints = (rel).getBendPoints(); >- LayoutBendPoint bp; >- SimpleNode startEntity = (SimpleNode) rel.getSourceInLayout(); >- SimpleNode destEntity = null; >- >- double srcX = startEntity.getX() + startEntity.getWidth() / 2.0; >- double srcY = startEntity.getY() + startEntity.getHeight() / 2.0; >- for (int i = 1; i < bendPoints.length - 1; i++) { >- bp = bendPoints[i]; >- destEntity = new SimpleNode(DUMMY_TITLE, bp.getX(), bp.getY(), 0.01, 0.01); >- drawEdge(srcX, srcY, bp.getX(), bp.getY(), rel, gcBuffer); >- startEntity = destEntity; >- srcX = startEntity.getX(); >- srcY = startEntity.getY(); >- } >- return destEntity; >- } >- >- } >- >-} >Index: src/org/eclipse/zest/layouts/exampleUses/SimpleSwingExample.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/exampleUses/SimpleSwingExample.java >diff -N src/org/eclipse/zest/layouts/exampleUses/SimpleSwingExample.java >--- src/org/eclipse/zest/layouts/exampleUses/SimpleSwingExample.java 12 Sep 2007 20:44:38 -0000 1.15 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,702 +0,0 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts.exampleUses; >- >-import java.awt.BasicStroke; >-import java.awt.BorderLayout; >-import java.awt.Color; >-import java.awt.Cursor; >-import java.awt.Dimension; >-import java.awt.Graphics; >-import java.awt.Graphics2D; >-import java.awt.Point; >-import java.awt.Polygon; >-import java.awt.RenderingHints; >-import java.awt.Shape; >-import java.awt.Stroke; >-import java.awt.Toolkit; >-import java.awt.event.ActionEvent; >-import java.awt.event.ActionListener; >-import java.awt.event.MouseAdapter; >-import java.awt.event.MouseEvent; >-import java.awt.event.MouseMotionListener; >-import java.awt.event.WindowAdapter; >-import java.awt.event.WindowEvent; >-import java.awt.geom.AffineTransform; >-import java.awt.geom.GeneralPath; >-import java.awt.geom.Point2D; >-import java.awt.geom.Rectangle2D; >-import java.lang.reflect.InvocationTargetException; >-import java.util.ArrayList; >-import java.util.Comparator; >-import java.util.Iterator; >-import java.util.List; >- >-import javax.swing.JButton; >-import javax.swing.JFrame; >-import javax.swing.JLabel; >-import javax.swing.JPanel; >-import javax.swing.JScrollPane; >-import javax.swing.JToggleButton; >-import javax.swing.JToolBar; >-import javax.swing.SwingUtilities; >- >-import org.eclipse.zest.layouts.InvalidLayoutConfiguration; >-import org.eclipse.zest.layouts.LayoutAlgorithm; >-import org.eclipse.zest.layouts.LayoutBendPoint; >-import org.eclipse.zest.layouts.LayoutEntity; >-import org.eclipse.zest.layouts.LayoutRelationship; >-import org.eclipse.zest.layouts.LayoutStyles; >-import org.eclipse.zest.layouts.algorithms.GridLayoutAlgorithm; >-import org.eclipse.zest.layouts.algorithms.HorizontalLayoutAlgorithm; >-import org.eclipse.zest.layouts.algorithms.HorizontalTreeLayoutAlgorithm; >-import org.eclipse.zest.layouts.algorithms.RadialLayoutAlgorithm; >-import org.eclipse.zest.layouts.algorithms.SpringLayoutAlgorithm; >-import org.eclipse.zest.layouts.algorithms.TreeLayoutAlgorithm; >-import org.eclipse.zest.layouts.algorithms.VerticalLayoutAlgorithm; >-import org.eclipse.zest.layouts.exampleStructures.SimpleNode; >-import org.eclipse.zest.layouts.exampleStructures.SimpleRelationship; >-import org.eclipse.zest.layouts.progress.ProgressEvent; >-import org.eclipse.zest.layouts.progress.ProgressListener; >- >-/** >- * @author Rob Lintern >- * @author Chris Bennett >- * >- * A simple example of using layout algorithms with a Swing application. >- */ >-public class SimpleSwingExample { >- private static final Color NODE_NORMAL_COLOR = new Color(225, 225, 255); >- private static final Color NODE_SELECTED_COLOR = new Color(255, 125, 125); >- //private static final Color NODE_ADJACENT_COLOR = new Color (255, 200, 125); >- private static final Color BORDER_NORMAL_COLOR = new Color(0, 0, 0); >- private static final Color BORDER_SELECTED_COLOR = new Color(255, 0, 0); >- //private static final Color BORDER_ADJACENT_COLOR = new Color (255, 128, 0); >- private static final Stroke BORDER_NORMAL_STROKE = new BasicStroke(1.0f); >- private static final Stroke BORDER_SELECTED_STROKE = new BasicStroke(2.0f); >- private static final Color RELATIONSHIP_NORMAL_COLOR = Color.BLUE; >- //private static final Color RELATIONSHIP_HIGHLIGHT_COLOR = new Color (255, 200, 125); >- >- public static SpringLayoutAlgorithm SPRING = new SpringLayoutAlgorithm(LayoutStyles.NONE); >- public static TreeLayoutAlgorithm TREE_VERT = new TreeLayoutAlgorithm(LayoutStyles.NONE); >- public static HorizontalTreeLayoutAlgorithm TREE_HORIZ = new HorizontalTreeLayoutAlgorithm(LayoutStyles.NONE); >- public static RadialLayoutAlgorithm RADIAL = new RadialLayoutAlgorithm(LayoutStyles.NONE); >- public static GridLayoutAlgorithm GRID = new GridLayoutAlgorithm(LayoutStyles.NONE); >- public static HorizontalLayoutAlgorithm HORIZ = new HorizontalLayoutAlgorithm(LayoutStyles.NONE); >- public static VerticalLayoutAlgorithm VERT = new VerticalLayoutAlgorithm(LayoutStyles.NONE); >- >- private List algorithms = new ArrayList(); >- private List algorithmNames = new ArrayList(); >- >- private static final int INITIAL_PANEL_WIDTH = 700; >- private static final int INITIAL_PANEL_HEIGHT = 500; >- >- private static final boolean RENDER_HIGH_QUALITY = true; >- >- private static final double INITIAL_NODE_WIDTH = 20; >- private static final double INITIAL_NODE_HEIGHT = 20; >- private static final int ARROW_HALF_WIDTH = 4; >- private static final int ARROW_HALF_HEIGHT = 6; >- private static final Shape ARROW_SHAPE = new Polygon(new int[] { -ARROW_HALF_HEIGHT, ARROW_HALF_HEIGHT, -ARROW_HALF_HEIGHT }, new int[] { -ARROW_HALF_WIDTH, 0, ARROW_HALF_WIDTH }, 3); >- private static final Stroke ARROW_BORDER_STROKE = new BasicStroke(0.5f); >- private static final Color ARROW_HEAD_FILL_COLOR = new Color(125, 255, 125); >- private static final Color ARROW_HEAD_BORDER_COLOR = Color.BLACK; >- >- public static final String DEFAULT_NODE_SHAPE = "oval"; >- >- private long updateGUICount = 0; >- >- private JFrame mainFrame; >- private JPanel mainPanel; >- private List entities; >- private List relationships; >- private JToolBar toolBar; >- private JLabel lblProgress; >- private JToggleButton btnContinuous; >- private JToggleButton btnAsynchronous; >- private JButton btnStop; >- >- private LayoutAlgorithm currentLayoutAlgorithm; >- protected String currentLayoutAlgorithmName; >- protected SimpleNode selectedEntity; >- protected Point mouseDownPoint; >- protected Point selectedEntityPositionAtMouseDown; >- private long idCount; >- protected String currentNodeShape = DEFAULT_NODE_SHAPE; // e.g., oval, rectangle >- >- public SimpleSwingExample() { >- >- } >- >- protected void addAlgorithm(LayoutAlgorithm algorithm, String name, boolean animate) { >- algorithms.add(algorithm); >- algorithmNames.add(name); >- } >- >- public void start() { >- >- mainFrame = new JFrame("Simple Swing Layout Example"); >- toolBar = new JToolBar(); >- mainFrame.getContentPane().setLayout(new BorderLayout()); >- mainFrame.getContentPane().add(toolBar, BorderLayout.NORTH); >- lblProgress = new JLabel("Progress: "); >- mainFrame.getContentPane().add(lblProgress, BorderLayout.SOUTH); >- >- createMainPanel(); >- mainFrame.addWindowListener(new WindowAdapter() { >- public void windowClosing(WindowEvent e) { >- stop(); >- mainFrame.dispose(); >- >- } >- }); >- >- btnContinuous = new JToggleButton("continuous", false); >- btnAsynchronous = new JToggleButton("asynchronous", false); >- >- toolBar.add(btnContinuous); >- toolBar.add(btnAsynchronous); >- >- btnStop = new JButton("Stop"); >- btnStop.addActionListener(new ActionListener() { >- public void actionPerformed(ActionEvent e) { >- stop(); >- } >- }); >- toolBar.add(btnStop); >- >- JButton btnCreateGraph = new JButton("New graph"); >- btnCreateGraph.addActionListener(new ActionListener() { >- public void actionPerformed(ActionEvent e) { >- stop(); >- createGraph(true); >- } >- }); >- toolBar.add(btnCreateGraph); >- JButton btnCreateTree = new JButton("New tree"); >- btnCreateTree.addActionListener(new ActionListener() { >- public void actionPerformed(ActionEvent e) { >- stop(); >- createGraph(false); >- } >- }); >- toolBar.add(btnCreateTree); >- >- createGraph(false); >- >- Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); >- mainFrame.setLocation((int) (screenSize.getWidth() - INITIAL_PANEL_WIDTH) / 2, (int) (screenSize.getHeight() - INITIAL_PANEL_HEIGHT) / 2); >- mainFrame.pack(); >- mainFrame.setVisible(true); >- mainFrame.repaint(); >- >- try { >- SwingUtilities.invokeAndWait(new Runnable() { >- >- public void run() { >- SPRING = new SpringLayoutAlgorithm(LayoutStyles.NONE); >- TREE_VERT = new TreeLayoutAlgorithm(LayoutStyles.NONE); >- TREE_HORIZ = new HorizontalTreeLayoutAlgorithm(LayoutStyles.NONE); >- RADIAL = new RadialLayoutAlgorithm(LayoutStyles.NONE); >- GRID = new GridLayoutAlgorithm(LayoutStyles.NONE); >- HORIZ = new HorizontalLayoutAlgorithm(LayoutStyles.NONE); >- VERT = new VerticalLayoutAlgorithm(LayoutStyles.NONE); >- >- SPRING.setIterations(1000); >- // initialize layouts >- TREE_VERT.setComparator(new Comparator() { >- public int compare(Object o1, Object o2) { >- if (o1 instanceof Comparable && o2 instanceof Comparable) { >- return ((Comparable) o1).compareTo(o2); >- } >- return 0; >- } >- >- }); >- GRID.setRowPadding(20); >- addAlgorithm(SPRING, "Spring", false); >- addAlgorithm(TREE_VERT, "Tree-V", false); >- addAlgorithm(TREE_HORIZ, "Tree-H", false); >- addAlgorithm(RADIAL, "Radial", false); >- addAlgorithm(GRID, "Grid", false); >- addAlgorithm(HORIZ, "Horiz", false); >- addAlgorithm(VERT, "Vert", false); >- >- for (int i = 0; i < algorithms.size(); i++) { >- final LayoutAlgorithm algorithm = (LayoutAlgorithm) algorithms.get(i); >- final String algorithmName = (String) algorithmNames.get(i); >- //final boolean algorithmAnimate = ((Boolean)algorithmAnimates.get(i)).booleanValue(); >- JButton algorithmButton = new JButton(algorithmName); >- algorithmButton.addActionListener(new ActionListener() { >- public void actionPerformed(ActionEvent e) { >- currentLayoutAlgorithm = algorithm; >- currentLayoutAlgorithmName = algorithmName; >- algorithm.setEntityAspectRatio((double) mainPanel.getWidth() / (double) mainPanel.getHeight()); >- //animate = algorithmAnimate; >- performLayout(); >- } >- }); >- toolBar.add(algorithmButton); >- } >- } >- }); >- } catch (InterruptedException e1) { >- // TODO Auto-generated catch block >- e1.printStackTrace(); >- } catch (InvocationTargetException e1) { >- // TODO Auto-generated catch block >- e1.printStackTrace(); >- } >- >- } >- >- private void stop() { >- if (currentLayoutAlgorithm != null && currentLayoutAlgorithm.isRunning()) { >- currentLayoutAlgorithm.stop(); >- } >- } >- >- protected void performLayout() { >- stop(); >- final Cursor cursor = mainFrame.getCursor(); >- updateGUICount = 0; >- placeRandomly(); >- final boolean continuous = btnContinuous.isSelected(); >- final boolean asynchronous = btnAsynchronous.isSelected(); >- ProgressListener progressListener = new ProgressListener() { >- public void progressUpdated(final ProgressEvent e) { >- //if (asynchronous) { >- updateGUI(); >- //} >- lblProgress.setText("Progress: " + e.getStepsCompleted() + " of " + e.getTotalNumberOfSteps() + " completed ..."); >- lblProgress.paintImmediately(0, 0, lblProgress.getWidth(), lblProgress.getHeight()); >- } >- >- public void progressStarted(ProgressEvent e) { >- if (!asynchronous) { >- mainFrame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); >- } >- lblProgress.setText("Layout started ..."); >- lblProgress.paintImmediately(0, 0, lblProgress.getWidth(), lblProgress.getHeight()); >- } >- >- public void progressEnded(ProgressEvent e) { >- lblProgress.setText("Layout completed ..."); >- lblProgress.paintImmediately(0, 0, lblProgress.getWidth(), lblProgress.getHeight()); >- currentLayoutAlgorithm.removeProgressListener(this); >- if (!asynchronous) { >- mainFrame.setCursor(cursor); >- } >- } >- }; >- >- currentLayoutAlgorithm.addProgressListener(progressListener); >- >- try { >- final LayoutEntity[] layoutEntities = new LayoutEntity[entities.size()]; >- entities.toArray(layoutEntities); >- final LayoutRelationship[] layoutRelationships = new LayoutRelationship[relationships.size()]; >- relationships.toArray(layoutRelationships); >- SwingUtilities.invokeLater(new Runnable() { >- public void run() { >- try { >- currentLayoutAlgorithm.applyLayout(layoutEntities, layoutRelationships, 0, 0, mainPanel.getWidth(), mainPanel.getHeight(), asynchronous, continuous); >- } catch (InvalidLayoutConfiguration e) { >- // TODO Auto-generated catch block >- e.printStackTrace(); >- } >- >- } >- >- }); >- //if (!animate) { >- updateGUI(); >- //} >- // reset >- currentNodeShape = DEFAULT_NODE_SHAPE; >- } catch (StackOverflowError e) { >- e.printStackTrace(); >- } finally { >- } >- } >- >- private void createMainPanel() { >- >- mainPanel = new MainPanel(); // see below for class definition >- mainPanel.setPreferredSize(new Dimension(INITIAL_PANEL_WIDTH, INITIAL_PANEL_HEIGHT)); >- mainPanel.setBackground(Color.WHITE); >- mainPanel.setLayout(null); >- mainFrame.getContentPane().add(new JScrollPane(mainPanel), BorderLayout.CENTER); >- >- mainPanel.addMouseListener(new MouseAdapter() { >- public void mousePressed(MouseEvent e) { >- selectedEntity = null; >- for (Iterator iter = entities.iterator(); iter.hasNext() && selectedEntity == null;) { >- SimpleNode entity = (SimpleNode) iter.next(); >- double x = entity.getX(); >- double y = entity.getY(); >- double w = entity.getWidth(); >- double h = entity.getHeight(); >- Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h); >- if (rect.contains(e.getX(), e.getY())) { >- selectedEntity = entity; >- } >- } >- if (selectedEntity != null) { >- mouseDownPoint = e.getPoint(); >- selectedEntityPositionAtMouseDown = new Point((int) selectedEntity.getX(), (int) selectedEntity.getY()); >- } else { >- mouseDownPoint = null; >- selectedEntityPositionAtMouseDown = null; >- } >- updateGUI(); >- } >- >- public void mouseReleased(MouseEvent e) { >- selectedEntity = null; >- mouseDownPoint = null; >- selectedEntityPositionAtMouseDown = null; >- updateGUI(); >- } >- }); >- >- mainPanel.addMouseMotionListener(new MouseMotionListener() { >- public void mouseDragged(MouseEvent e) { >- // if (selectedEntity != null) { >- // //TODO: Add mouse moving >- // //selectedEntity.setLocationInLayout(selectedEntityPositionAtMouseDown.x + dx, selectedEntityPositionAtMouseDown.y + dy); >- // updateGUI(); >- // } >- } >- >- public void mouseMoved(MouseEvent e) { >- } >- }); >- } >- >- private void createGraph(boolean addNonTreeRels) { >- entities = new ArrayList(); >- relationships = new ArrayList(); >- selectedEntity = null; >- >- createTreeGraph(2, 4, 2, 5, true, true, addNonTreeRels); >- // createCustomGraph(); >- >- placeRandomly(); >- mainPanel.repaint(); >- } >- >- /** >- * >- * @param maxLevels Max number of levels wanted in tree >- * @param maxChildren Max number of children for each node in the tree >- * @param randomNumChildren Whether or not to pick random number of levels (from 1 to maxLevels) and >- * random number of children (from 1 to maxChildren) >- */ >- private void createTreeGraph(int minChildren, int maxChildren, int minLevels, int maxLevels, boolean randomNumChildren, boolean randomLevels, boolean addNonTreeRels) { >- LayoutEntity currentParent = createSimpleNode(getNextID()); >- entities.add(currentParent); >- createTreeGraphRecursive(currentParent, minChildren, maxChildren, minLevels, maxLevels, 1, randomNumChildren, randomLevels, addNonTreeRels); >- } >- >- private void createTreeGraphRecursive(LayoutEntity currentParentNode, int minChildren, int maxChildren, int minLevel, int maxLevel, int level, boolean randomNumChildren, boolean randomLevels, boolean addNonTreeRels) { >- if (level > maxLevel) { >- return; >- } >- if (randomLevels) { >- if (level > minLevel) { >- double zeroToOne = Math.random(); >- if (zeroToOne < 0.75) { >- return; >- } >- } >- } >- int numChildren = randomNumChildren ? Math.max(minChildren, (int) (Math.random() * maxChildren + 1)) : maxChildren; >- for (int i = 0; i < numChildren; i++) { >- LayoutEntity newNode = createSimpleNode(getNextID()); >- entities.add(newNode); >- if (addNonTreeRels && entities.size() % 5 == 0) { >- int index = (int) (Math.random() * entities.size()); >- LayoutRelationship rel = new SimpleRelationship((LayoutEntity) entities.get(index), newNode, false); >- relationships.add(rel); >- } >- LayoutRelationship rel = new SimpleRelationship(currentParentNode, newNode, false); >- relationships.add(rel); >- createTreeGraphRecursive(newNode, minChildren, maxChildren, minLevel, maxLevel, level + 1, randomNumChildren, randomLevels, addNonTreeRels); >- } >- } >- >- /** >- * Call this from createGraph in place of createTreeGraph >- * this for debugging and testing. >- */ >- /* private void createCustomGraph() { >- LayoutEntity A = createSimpleNode("1"); >- LayoutEntity B = createSimpleNode("10"); >- LayoutEntity _1 = createSimpleNode("100"); >- entities.add(A); >- entities.add(B); >- entities.add(_1); >- relationships.add(new SimpleRelationship (A, B, false)); >- relationships.add(new SimpleRelationship (A, _1, false)); >- relationships.add(new SimpleRelationship (_1, A, false)); >- } >- */ >- private String getNextID() { >- String id = "" + idCount; >- idCount++; >- return id; >- } >- >- /** Places nodes randomly on the screen **/ >- private void placeRandomly() { >- for (Iterator iter = entities.iterator(); iter.hasNext();) { >- SimpleNode simpleNode = (SimpleNode) iter.next(); >- double x = Math.random() * INITIAL_PANEL_WIDTH - INITIAL_NODE_WIDTH; >- double y = Math.random() * INITIAL_PANEL_HEIGHT - INITIAL_NODE_HEIGHT; >- simpleNode.setLocationInLayout(x, y); >- simpleNode.setSizeInLayout(INITIAL_NODE_WIDTH, INITIAL_NODE_HEIGHT); >- } >- } >- >- /** >- * Creates a SimpleNode >- * @param name >- * @return >- */ >- private SimpleNode createSimpleNode(String name) { >- SimpleNode simpleNode = new SimpleNode(name); >- return simpleNode; >- } >- >- private void updateGUI() { >- updateGUICount++; >- if (updateGUICount > 0) { >- mainPanel.paintImmediately(0, 0, mainPanel.getWidth(), mainPanel.getHeight()); >- } >- } >- >- private static Point2D.Double getEllipseIntersectionPoint(double theta, double ellipseWidth, double ellipseHeight) { >- double nhalfw = ellipseWidth / 2.0; // half elllipse width >- double nhalfh = ellipseHeight / 2.0; // half ellipse height >- double tanTheta = Math.tan(theta); >- >- double a = nhalfw; >- double b = nhalfh; >- double x = (a * b) / Math.sqrt(Math.pow(b, 2) + Math.pow(a, 2) * Math.pow(tanTheta, 2)); >- if ((theta > Math.PI / 2.0 && theta < 1.5 * Math.PI) || (theta < -Math.PI / 2.0 && theta > -1.5 * Math.PI)) { >- x = -x; >- } >- double y = tanTheta * x; >- Point2D.Double p = new Point2D.Double(x, y); >- return p; >- } >- >- public static void main(String[] args) { >- (new SimpleSwingExample()).start(); >- } >- >- /** >- * A JPanel that provides entity and relationship rendering >- * Instead of letting Swing paint all the JPanels for us, we will just do our own painting here >- */ >- private class MainPanel extends JPanel { >- >- private static final long serialVersionUID = 1; >- >- protected void paintChildren(Graphics g) { >- if (g instanceof Graphics2D && RENDER_HIGH_QUALITY) { >- ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); >- ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); >- ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); >- ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); >- } >- >- // paint the nodes >- for (Iterator iter = entities.iterator(); iter.hasNext();) { >- paintEntity((SimpleNode) iter.next(), g); >- } >- >- // paint the relationships >- for (Iterator iter = relationships.iterator(); iter.hasNext();) { >- paintRelationship((LayoutRelationship) iter.next(), g); >- } >- } >- >- private void paintEntity(SimpleNode entity, Graphics g) { >- boolean isSelected = selectedEntity != null && selectedEntity.equals(entity); >- g.setColor(isSelected ? NODE_SELECTED_COLOR : NODE_NORMAL_COLOR); >- if (currentNodeShape.equals("rectangle")) { >- g.fillRect((int) entity.getX(), (int) entity.getY(), (int) entity.getWidth(), (int) entity.getHeight()); >- } else { // default >- g.fillOval((int) entity.getX(), (int) entity.getY(), (int) entity.getWidth(), (int) entity.getHeight()); >- } >- g.setColor(isSelected ? BORDER_SELECTED_COLOR : BORDER_NORMAL_COLOR); >- String name = entity.toString(); >- Rectangle2D nameBounds = g.getFontMetrics().getStringBounds(name, g); >- g.drawString(name, (int) (entity.getX() + entity.getWidth() / 2.0 - nameBounds.getWidth() / 2.0), (int) (entity.getY() + entity.getHeight() / 2.0 + nameBounds.getHeight() / 2.0));//- nameBounds.getHeight() - nameBounds.getY())); >- if (g instanceof Graphics2D) { >- ((Graphics2D) g).setStroke(isSelected ? BORDER_SELECTED_STROKE : BORDER_NORMAL_STROKE); >- } >- if (currentNodeShape.equals("rectangle")) { >- g.drawRect((int) entity.getX(), (int) entity.getY(), (int) entity.getWidth(), (int) entity.getHeight()); >- } else { // default >- g.drawOval((int) entity.getX(), (int) entity.getY(), (int) entity.getWidth(), (int) entity.getHeight()); >- } >- } >- >- private void paintRelationship(LayoutRelationship rel, Graphics g) { >- >- SimpleNode src = (SimpleNode) rel.getSourceInLayout(); >- SimpleNode dest = (SimpleNode) rel.getDestinationInLayout(); >- >- // Add bend points if required >- if (((SimpleRelationship) rel).getBendPoints() != null && ((SimpleRelationship) rel).getBendPoints().length > 0) { >- drawBendPoints(rel, g); >- } else { >- double srcX = src.getX() + src.getWidth() / 2.0; >- double srcY = src.getY() + src.getHeight() / 2.0; >- double destX = dest.getX() + dest.getWidth() / 2.0; >- double destY = dest.getY() + dest.getHeight() / 2.0; >- double dx = getLength(srcX, destX); >- double dy = getLength(srcY, destY); >- double theta = Math.atan2(dy, dx); >- drawRelationship(src, dest, theta, srcX, srcY, destX, destY, g); >- >- // draw an arrow in the middle of the line >- drawArrow(theta, srcX, srcY, dx, dy, g); >- } >- } >- >- /** >- * Draw a line from the edge of the src node to the edge of the destination node >- */ >- private void drawRelationship(SimpleNode src, SimpleNode dest, double theta, double srcX, double srcY, double destX, double destY, Graphics g) { >- >- double reverseTheta = theta > 0.0d ? theta - Math.PI : theta + Math.PI; >- >- Point2D.Double srcIntersectionP = getEllipseIntersectionPoint(theta, src.getWidth(), src.getHeight()); >- >- Point2D.Double destIntersectionP = getEllipseIntersectionPoint(reverseTheta, dest.getWidth(), dest.getHeight()); >- >- drawRelationship(srcX + srcIntersectionP.getX(), srcY + srcIntersectionP.getY(), destX + destIntersectionP.getX(), destY + destIntersectionP.getY(), g); >- } >- >- /** >- * Draw a line from specified source to specified destination >- */ >- private void drawRelationship(double srcX, double srcY, double destX, double destY, Graphics g) { >- g.setColor(RELATIONSHIP_NORMAL_COLOR); >- g.drawLine((int) srcX, (int) srcY, (int) destX, (int) destY); >- } >- >- private void drawArrow(double theta, double srcX, double srcY, double dx, double dy, Graphics g) { >- AffineTransform tx = new AffineTransform(); >- double arrX = srcX + (dx) / 2.0; >- double arrY = srcY + (dy) / 2.0; >- tx.translate(arrX, arrY); >- tx.rotate(theta); >- Shape arrowTx = tx.createTransformedShape(ARROW_SHAPE); >- if (g instanceof Graphics2D) { >- g.setColor(ARROW_HEAD_FILL_COLOR); >- ((Graphics2D) g).fill(arrowTx); >- ((Graphics2D) g).setStroke(ARROW_BORDER_STROKE); >- g.setColor(ARROW_HEAD_BORDER_COLOR); >- ((Graphics2D) g).draw(arrowTx); >- } >- } >- >- /** >- * Get the length of a line ensuring it is not too small to render >- * @param start >- * @param end >- * @return >- */ >- private double getLength(double start, double end) { >- double length = end - start; >- // make sure dx is not zero or too small >- if (length < 0.01 && length > -0.01) { >- if (length > 0) { >- length = 0.01; >- } else if (length < 0) { >- length = -0.01; >- } >- } >- return length; >- } >- >- /** >- * Draw a line from specified source to specified destination >- */ >- private void drawCurvedRelationship(double srcX, double srcY, double control1X, double control1Y, double control2X, double control2Y, double destX, double destY, Graphics g) { >- GeneralPath shape = new GeneralPath(); >- shape.moveTo((float) srcX, (float) srcY); >- shape.curveTo((float) control1X, (float) control1Y, (float) control2X, (float) control2Y, (float) destX, (float) destY); >- g.setColor(RELATIONSHIP_NORMAL_COLOR); >- ((Graphics2D) g).draw(shape); >- } >- >- /** >- * Draws a set of lines between bendpoints, returning the last bendpoint >- * drawn. Note that this assumes the first and last bendpoints are actually >- * the source node and destination node centre points. >- * @param relationship >- * @param bendNodes >- * @param bendEdges >- * @return the last bendpoint entity or null if there are no bendpoints >- */ >- private void drawBendPoints(LayoutRelationship rel, Graphics g) { >- final String DUMMY_TITLE = "dummy"; >- LayoutBendPoint bp; >- >- SimpleNode startEntity = (SimpleNode) rel.getSourceInLayout(); >- SimpleNode destEntity = (SimpleNode) rel.getDestinationInLayout(); >- double srcX = startEntity.getX(); >- double srcY = startEntity.getY(); >- >- // Transform the bendpoints to this coordinate system >- LayoutBendPoint[] bendPoints = ((SimpleRelationship) rel).getBendPoints(); >- >- srcX = bendPoints[1].getX(); >- srcY = bendPoints[1].getY(); >- int bpNum = 2; >- while (bpNum < bendPoints.length - 1) { // ignore first and last bendpoints (src and dest) >- int currentBpNum = bpNum; >- bp = bendPoints[bpNum]; >- if (bp.getIsControlPoint()) { >- if (bendPoints[bpNum + 1].getIsControlPoint()) { >- destEntity = new SimpleNode(DUMMY_TITLE, bendPoints[bpNum + 2].getX(), bendPoints[bpNum + 2].getY(), 0.01, 0.01); >- drawCurvedRelationship(srcX, srcY, bp.getX(), bp.getY(), bendPoints[bpNum + 1].getX(), bendPoints[bpNum + 1].getY(), bendPoints[bpNum + 2].getX(), bendPoints[bpNum + 2].getY(), g); >- bpNum += 4; >- } else { >- destEntity = new SimpleNode(DUMMY_TITLE, bp.getX(), bp.getY(), 0.01, 0.01); >- } >- } else { >- drawRelationship(srcX, srcY, bp.getX(), bp.getY(), g); >- bpNum++; >- destEntity = new SimpleNode(DUMMY_TITLE, bp.getX(), bp.getY(), 0.01, 0.01); >- } >- startEntity = destEntity; >- if (currentBpNum == bendPoints.length - 2) { // last point >- // draw an arrow in the middle of the line >- double dx = getLength(srcX, destEntity.getX()); >- double dy = getLength(srcY, destEntity.getY()); >- double theta = Math.atan2(dy, dx); >- drawArrow(theta, srcX, srcY, dx, dy, g); >- } else { >- srcX = startEntity.getX(); >- srcY = startEntity.getY(); >- } >- } >- >- } >- } >-} >Index: src/org/eclipse/zest/layouts/interfaces/ConnectionLayout.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/interfaces/ConnectionLayout.java >diff -N src/org/eclipse/zest/layouts/interfaces/ConnectionLayout.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/zest/layouts/interfaces/ConnectionLayout.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,48 @@ >+/******************************************************************************* >+ * Copyright (c) 2009-2010 Mateusz Matela and others. 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: Mateusz Matela - initial API and implementation >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.layouts.interfaces; >+ >+ >+public interface ConnectionLayout { >+ >+ public NodeLayout getSource(); >+ >+ public NodeLayout getTarget(); >+ >+ /** >+ * >+ * @return weight assigned to this connection >+ */ >+ public double getWeight(); >+ >+ /** >+ * Checks if this connection is directed. For undirected connections, source >+ * and target nodes should be considered just adjacent nodes without >+ * dividing to source/target. >+ * >+ * @return true if this connection is directed >+ */ >+ public boolean isDirected(); >+ >+ /** >+ * Changes the visibility state of this connection. >+ * >+ * @param visible >+ * true if this connection should be visible, false otherwise >+ */ >+ public void setVisible(boolean visible); >+ >+ /** >+ * Checks the visibility state of this connection. >+ * >+ * @return true if this connection is visible, false otherwise >+ */ >+ public boolean isVisible(); >+} >Index: src/org/eclipse/zest/layouts/interfaces/ContextListener.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/interfaces/ContextListener.java >diff -N src/org/eclipse/zest/layouts/interfaces/ContextListener.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/zest/layouts/interfaces/ContextListener.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,74 @@ >+/******************************************************************************* >+ * Copyright (c) 2009-2010 Mateusz Matela and others. 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: Mateusz Matela - initial API and implementation >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.layouts.interfaces; >+ >+import org.eclipse.zest.layouts.LayoutAlgorithm; >+ >+public interface ContextListener { >+ public class Stub implements ContextListener { >+ >+ public boolean boundsChanged(LayoutContext context) { >+ return false; >+ } >+ >+ public void backgroundEnableChanged(LayoutContext context) { >+ // do nothing >+ } >+ >+ public boolean pruningEnablementChanged(LayoutContext context) { >+ return false; >+ } >+ >+ } >+ >+ /** >+ * This method is called whenever the bounds available in a layout context >+ * change. >+ * >+ * If true is returned, it means that the receiving listener has intercepted >+ * this event. Intercepted events will not be passed to the rest of the >+ * listeners. If the event is not intercepted by any listener, >+ * {@link LayoutAlgorithm#applyLayout() applyLayout()} will be called on the >+ * context's main algorithm. >+ * >+ * @param context >+ * the layout context that fired the event >+ * @return true if no further operations after this event are required >+ */ >+ public boolean boundsChanged(LayoutContext context); >+ >+ /** >+ * This method is called whenever graph pruning is enabled or disabled in a >+ * layout context. >+ * >+ * If true is returned, it means that the receiving listener has intercepted >+ * this event. Intercepted events will not be passed to the rest of the >+ * listeners. If the event is not intercepted by any listener, >+ * {@link LayoutAlgorithm#applyLayout() applyLayout()} will be called on the >+ * context's main algorithm. >+ * >+ * @param context >+ * the layout context that fired the event >+ * @return true if no further operations after this event are required >+ */ >+ public boolean pruningEnablementChanged(LayoutContext context); >+ >+ /** >+ * This method is called whenever background layout is enabled or disabled >+ * in a layout context. If the receiving listener is related to a layout >+ * algorithm that performs layout in reaction to events, it should turn >+ * automatic flush of changes on or off. Also, eventual additional threads >+ * responsible for layout should be stopped or started accordingly. >+ * >+ * @param context >+ * the layout context that fired the event >+ */ >+ public void backgroundEnableChanged(LayoutContext context); >+} >Index: src/org/eclipse/zest/layouts/interfaces/EntityLayout.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/interfaces/EntityLayout.java >diff -N src/org/eclipse/zest/layouts/interfaces/EntityLayout.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/zest/layouts/interfaces/EntityLayout.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,106 @@ >+/******************************************************************************* >+ * Copyright (c) 2009-2010 Mateusz Matela and others. 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: Mateusz Matela - initial API and implementation >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.layouts.interfaces; >+ >+import org.eclipse.zest.layouts.dataStructures.DisplayIndependentDimension; >+import org.eclipse.zest.layouts.dataStructures.DisplayIndependentPoint; >+ >+/** >+ * A common interface for entities that are displayed on a graph, that is >+ * {@link NodeLayout nodes} and {@link SubgraphLayout subgraphs}. >+ */ >+public interface EntityLayout { >+ >+ /** >+ * Returns a point laying in the center of this entity. Any subsequent >+ * changes to the returned point won't affect this node. >+ * >+ * @return position of the center of this node >+ */ >+ public DisplayIndependentPoint getLocation(); >+ >+ /** >+ * Sets the position of this entity. The node will be moved so that it's >+ * center is located in the given point. >+ * >+ * @param x >+ * the x-position >+ * @param y >+ * the y-position >+ */ >+ public void setLocation(double x, double y); >+ >+ public DisplayIndependentDimension getSize(); >+ >+ public void setSize(double width, double height); >+ >+ /** >+ * Returns aspect ratio that is preferred for this entity. Can be 0 if this >+ * node can't be resized anyway or it doesn't care about about its ratio. >+ * >+ * @return aspect ratio (width / height) >+ */ >+ public double getPreferredAspectRatio(); >+ >+ public boolean isResizable(); >+ >+ public boolean isMovable(); >+ >+ /** >+ * Returns all entities that are direct successors of this entity. Successor >+ * entities of an unpruned node N are: >+ * <ul> >+ * <li>all unpruned successor nodes of node N</li> >+ * <li>all subgraphs that are <code>GraphEntities</code> and contain at >+ * least one successor node of node N</li> >+ * </ul> >+ * Successor entities of a subgraph S that is a <code>GraphEntity</code> >+ * are: >+ * <ul> >+ * <li>all unpruned nodes that are successor of at least one node from >+ * subgraph S</li> >+ * <li>all subgraphs that are <code>GraphEntities</code> and contain at >+ * least one node that is a successor of at least one node from subgraph S</li> >+ * </ul> >+ * For subgraphs that are not <code>GraphEntities</code> an empty array will >+ * be returned.</br>Entities connected with this node by a bidirectional >+ * connection are considered both successors and predecessors. Any >+ * subsequent changes to the returned array do not affect this node. >+ * >+ * @return array of successors of this node >+ */ >+ public EntityLayout[] getSuccessingEntities(); >+ >+ /** >+ * Returns all entities that are direct predecessors of this entity. >+ * Predecessor entities of an unpruned node A are: >+ * <ul> >+ * <li>all unpruned predecessor nodes of node N</li> >+ * <li>all subgraphs that are <code>GraphEntities</code> and contain at >+ * least one predecessor node of node N</li> >+ * </ul> >+ * Successor entities of a subgraph S that is a <code>GraphEntity</code> >+ * are: >+ * <ul> >+ * <li>all unpruned nodes that are predecessor of at least one node from >+ * subgraph S</li> >+ * <li>all subgraphs that are <code>GraphEntities</code> and contain at >+ * least one node that is a predecessor of at least one node from subgraph S >+ * </li> >+ * </ul> >+ * For subgraphs that are not <code>GraphEntities</code> an empty array will >+ * be returned.</br>Entities connected with this node by a bidirectional >+ * connection are considered both successors and predecessors. Any >+ * subsequent changes to the returned array do not affect this node. >+ * >+ * @return array of predecessors of this node >+ */ >+ public EntityLayout[] getPredecessingEntities(); >+} >Index: src/org/eclipse/zest/layouts/interfaces/ExpandCollapseManager.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/interfaces/ExpandCollapseManager.java >diff -N src/org/eclipse/zest/layouts/interfaces/ExpandCollapseManager.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/zest/layouts/interfaces/ExpandCollapseManager.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,63 @@ >+/******************************************************************************* >+ * Copyright (c) 2009-2010 Mateusz Matela and others. 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: Mateusz Matela - initial API and implementation >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.layouts.interfaces; >+ >+/** >+ * A manager that controls expanding and collapsing nodes in a Graph. >+ */ >+public interface ExpandCollapseManager { >+ >+ /** >+ * Initializes the expansion state of all nodes in given layout context. The >+ * receiver can initialize its internal state related to the layout context >+ * and add its listeners if necessary. >+ * >+ * @param context >+ * the context to initialize >+ */ >+ public void initExpansion(LayoutContext context); >+ >+ /** >+ * Changes the expanded state of given node. It prunes/unprunes nodes and >+ * hides/shows connections in the graph according to its policy. If >+ * requested operation cannot be currently performed on the node, it does >+ * nothing. >+ * >+ * @param context >+ * context in which to perform the operation >+ * @param node >+ * node to expand or collapse >+ * @param expanded >+ * true to expand, false to collapse >+ */ >+ public void setExpanded(LayoutContext context, NodeLayout node, boolean expanded); >+ >+ /** >+ * Checks if given node can be expanded. >+ * >+ * @param context >+ * context containing the node >+ * @param node >+ * node to check >+ * @return >+ */ >+ public boolean canExpand(LayoutContext context, NodeLayout node); >+ >+ /** >+ * Checks if given node can be collapsed. >+ * >+ * @param context >+ * context containing the node >+ * @param node >+ * node to check >+ * @return >+ */ >+ public boolean canCollapse(LayoutContext context, NodeLayout node); >+} >\ No newline at end of file >Index: src/org/eclipse/zest/layouts/interfaces/GraphStructureListener.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/interfaces/GraphStructureListener.java >diff -N src/org/eclipse/zest/layouts/interfaces/GraphStructureListener.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/zest/layouts/interfaces/GraphStructureListener.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,119 @@ >+/******************************************************************************* >+ * Copyright (c) 2009-2010 Mateusz Matela and others. 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: Mateusz Matela - initial API and implementation >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.layouts.interfaces; >+ >+import org.eclipse.zest.layouts.LayoutAlgorithm; >+ >+public interface GraphStructureListener { >+ >+ public class Stub implements GraphStructureListener { >+ >+ public boolean nodeAdded(LayoutContext context, NodeLayout node) { >+ return false; >+ } >+ >+ public boolean nodeRemoved(LayoutContext context, NodeLayout node) { >+ return false; >+ } >+ >+ public boolean connectionAdded(LayoutContext context, ConnectionLayout connection) { >+ return false; >+ } >+ >+ public boolean connectionRemoved(LayoutContext context, ConnectionLayout connection) { >+ return false; >+ } >+ } >+ >+ /** >+ * This method is called whenever a node is added to a context. No separate >+ * events will be fired for eventual connections adjacent to the added node. >+ * >+ * If true is returned, it means that the receiving listener has intercepted >+ * this event. Intercepted events will not be passed to the rest of the >+ * listeners. If the event is not intercepted by any listener, >+ * {@link LayoutAlgorithm#applyLayout() applyLayout()} will be called on the >+ * context's main algorithm. >+ * >+ * @param context >+ * the layout context that fired the event >+ * @param node >+ * the added node >+ * @return true if no further operations after this event are required >+ */ >+ public boolean nodeAdded(LayoutContext context, NodeLayout node); >+ >+ /** >+ * This method is called whenever a node is removed from a context. No >+ * separate events will be fired for eventual connections adjacent to the >+ * removed node. >+ * >+ * If true is returned, it means that the receiving listener has intercepted >+ * this event. Intercepted events will not be passed to the rest of the >+ * listeners. If the event is not intercepted by any listener, >+ * {@link LayoutAlgorithm#applyLayout() applyLayout()} will be called on the >+ * context's main algorithm. >+ * >+ * @param context >+ * the context that fired the event >+ * @param node >+ * the removed node >+ * @return true if no further operations after this event are required >+ */ >+ public boolean nodeRemoved(LayoutContext context, NodeLayout node); >+ >+ /** >+ * This method is called whenever a connection is added to a context. It can >+ * be assumed that both source and target nodes of the added connection >+ * already exist in the context. >+ * >+ * This method will be called only if both nodes connected by added >+ * connection lay directly in the node container owned by the notifying >+ * layout context. >+ * >+ * If true is returned, it means that the receiving listener has intercepted >+ * this event. Intercepted events will not be passed to the rest of the >+ * listeners. If the event is not intercepted by any listener, >+ * {@link LayoutAlgorithm#applyLayout() applyLayout()} will be called on the >+ * context's main algorithm. >+ * >+ * @param context >+ * the context that fired the event >+ * @param connection >+ * the added connection >+ * @return true if no further operations after this event are required >+ */ >+ public boolean connectionAdded(LayoutContext context, ConnectionLayout connection); >+ >+ /** >+ * This method is called whenever a connection is removed from a context. It >+ * can be assumed that both source and target nodes of the removed >+ * connection still exist in the context and will not be removed along with >+ * it. >+ * >+ * This method will be called only if both nodes connected by removed >+ * connection lay directly in the node container owned by the notifying >+ * layout context. >+ * >+ * If true is returned, it means that the receiving listener has intercepted >+ * this event. Intercepted events will not be passed to the rest of the >+ * listeners. If the event is not intercepted by any listener, >+ * {@link LayoutAlgorithm#applyLayout() applyLayout()} will be called on the >+ * context's main algorithm. >+ * >+ * @param context >+ * the context that fired the event >+ * @param connection >+ * the added connection >+ * @return true if no further operations after this event are required >+ */ >+ public boolean connectionRemoved(LayoutContext context, ConnectionLayout connection); >+ >+} >Index: src/org/eclipse/zest/layouts/interfaces/LayoutContext.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/interfaces/LayoutContext.java >diff -N src/org/eclipse/zest/layouts/interfaces/LayoutContext.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/zest/layouts/interfaces/LayoutContext.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,229 @@ >+/******************************************************************************* >+ * Copyright (c) 2009-2010 Mateusz Matela and others. 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: Mateusz Matela - initial API and implementation >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.layouts.interfaces; >+ >+import org.eclipse.zest.layouts.LayoutAlgorithm; >+import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle; >+ >+/** >+ * Objects implementing LayoutContext interface are used for exchanging of >+ * information between layout algorithms and graphical objects displaying >+ * graphs. >+ */ >+public interface LayoutContext { >+ >+ /** >+ * Returns all the nodes that should be laid out. Replacing elements in the >+ * returned array does not affect this context. >+ * >+ * @return array of nodes to lay out >+ */ >+ public NodeLayout[] getNodes(); >+ >+ /** >+ * Returns all the connections between nodes that should be laid out. >+ * Replacing elements in the returned array does not affect this context. >+ * >+ * @return array of connections between nodes >+ */ >+ public ConnectionLayout[] getConnections(); >+ >+ /** >+ * Returns all entities that are currently placed on the graph, that is >+ * subgraphs and unpruned nodes. Replacing elements in the returned array >+ * does not affect this context. >+ * >+ * @return array of entities to layout >+ */ >+ public EntityLayout[] getEntities(); >+ >+ /** >+ * Returns all the connections between given source and target entities. If >+ * given entity is a subgraph, connections adjacent to each of its nodes >+ * will be included in the result. All the undirected nodes connecting the >+ * two nodes will be also included in the result. Replacing elements in the >+ * returned array does not affect this context. >+ * >+ * @param layoutEntity1 >+ * @param layoutEntity2 >+ * @return >+ */ >+ public ConnectionLayout[] getConnections(EntityLayout layoutEntity1, EntityLayout layoutEntity2); >+ >+ /** >+ * >+ * @return bounds in which the graph elements can be placed >+ */ >+ public DisplayIndependentRectangle getBounds(); >+ >+ /** >+ * >+ * @return true if a layout algorithm is allowed to place graph elements >+ * outside of suggested bounds >+ */ >+ public boolean isBoundsExpandable(); >+ >+ /** >+ * Returns all the subgraphs this context's nodes were pruned to. Replacing >+ * elements in the returned array does not affect this context. >+ * >+ * @return array of subgraphs (may be empty) >+ */ >+ public SubgraphLayout[] getSubgraphs(); >+ >+ /** >+ * Creates a subgraph containing given nodes and adds it to this context. If >+ * given nodes already belong to another subgraphs, they are removed from >+ * them prior to adding to the new subgraph. >+ * >+ * @param nodes >+ * nodes to add to the new subgraph >+ */ >+ public SubgraphLayout createSubgraph(NodeLayout[] nodes); >+ >+ /** >+ * >+ * @return true if this layout context allows pruning nodes into subgraphs >+ */ >+ public boolean isPruningEnabled(); >+ >+ /** >+ * Checks if this layout context allows layout algorithms to work >+ * continuously in background and change the layout with time or in reaction >+ * to some events. If background changes are not allowed, a layout algorithm >+ * can make changes in layout context only when >+ * {@link LayoutAlgorithm#applyLayout(boolean)} is called (otherwise a >+ * runtime exception will be thrown). >+ * >+ * @return true if background layout changes are enabled >+ */ >+ public boolean isBackgroundLayoutEnabled(); >+ >+ /** >+ * Sets the main layout algorithm for this context. Main algorithm will be >+ * used to relayout graph items using {@link LayoutAlgorithm#applyLayout()} >+ * after every event that is not intercepted by any listener. >+ * >+ * @param algorithm >+ */ >+ public void setMainLayoutAlgorithm(LayoutAlgorithm algorithm); >+ >+ /** >+ * >+ * @return the main algorithm of this context (see >+ * {@link #setMainLayoutAlgorithm(LayoutAlgorithm)} for details) >+ */ >+ public LayoutAlgorithm getMainLayoutAlgorithm(); >+ >+ /** >+ * Sets the expand/collapse manager for this context. The manger will be >+ * used to handle expansion related methods called on the owner of this >+ * context. >+ * >+ * @param expandCollapseManager >+ */ >+ public void setExpandCollapseManager(ExpandCollapseManager expandCollapseManager); >+ >+ /** >+ * >+ * @return current expand/collapse manager (can be null, which means that >+ * pruning is not enabled). >+ */ >+ public ExpandCollapseManager getExpandCollapseManager(); >+ >+ /** >+ * Adds a listener to the context that will be notified about changes in >+ * this context's layout, that is movement and resizing of nodes / >+ * subgraphs. The notifications will not include changes made with API >+ * included in layout related interfaces, so that layout algorithms won't be >+ * notified about changes they invoke. Only internal changes of the system >+ * will fire events. >+ * >+ * @param listener >+ * listener to add >+ */ >+ public void addLayoutListener(LayoutListener listener); >+ >+ /** >+ * Removes a layout listener from this context. >+ * >+ * @param listener >+ * listener to remove >+ */ >+ public void removeLayoutListener(LayoutListener listener); >+ >+ /** >+ * Adds a listener to the context that will be notified about changes in >+ * graph structure, that is addition and removal of nodes and connections. >+ * The notifications will not include changes made with API included in >+ * layout related interfaces, so that layout algorithms won't be notified >+ * about changes they invoke. Only internal changes of the system will fire >+ * events. >+ * >+ * @param listener >+ * listener to add >+ */ >+ public void addGraphStructureListener(GraphStructureListener listener); >+ >+ /** >+ * Removes a graph structure listener from this context. >+ * >+ * @param listener >+ * listener to remove >+ */ >+ public void removeGraphStructureListener(GraphStructureListener listener); >+ >+ /** >+ * Adds a listener to the context that will be notified about changes >+ * related to its configuration. >+ * >+ * @param listener >+ * listener to add >+ */ >+ public void addContextListener(ContextListener listener); >+ >+ /** >+ * Removes a context listener from this context. >+ * >+ * @param listener >+ * listener to remove >+ */ >+ public void removeContextListener(ContextListener listener); >+ >+ /** >+ * Adds a listener to the context that will be notified about changes in >+ * graph pruning, that is hiding and showing of nodes. The notifications >+ * will not include changes made with API included in layout related >+ * interfaces, so that layout algorithms won't be notified about changes >+ * they invoke. Only internal changes of the system will fire events. >+ * >+ * @param listener >+ * listener to add >+ */ >+ public void addPruningListener(PruningListener listener); >+ >+ /** >+ * Removes a pruning structure listener from this context. >+ * >+ * @param listener >+ * listener to remove >+ */ >+ public void removePruningListener(PruningListener listener); >+ >+ /** >+ * Causes all the changes made to elements in this context to affect the >+ * display. >+ * >+ * @param animationHint >+ * a hint for display mechanism indicating whether changes are >+ * major and should be animated (if true) or not. >+ */ >+ public void flushChanges(boolean animationHint); >+} >Index: src/org/eclipse/zest/layouts/interfaces/LayoutListener.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/interfaces/LayoutListener.java >diff -N src/org/eclipse/zest/layouts/interfaces/LayoutListener.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/zest/layouts/interfaces/LayoutListener.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,98 @@ >+/******************************************************************************* >+ * Copyright (c) 2009-2010 Mateusz Matela and others. 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: Mateusz Matela - initial API and implementation >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.layouts.interfaces; >+ >+import org.eclipse.zest.layouts.LayoutAlgorithm; >+ >+/** >+ * >+ * @author irbull >+ * >+ * @noextend This interface is not intended to be extended by clients. >+ * @noimplement This interface is not intended to be implemented by clients. >+ */ >+public interface LayoutListener { >+ >+ /** >+ * This method is called whenever location of a particular node is changed >+ * within observed context. This usually implicates change of position (the >+ * center of the node) and the receiver should be aware of it (no additional >+ * {@link #nodeMoved(LayoutContext, NodeLayout)} event will be fired). If >+ * true is returned, it means that the receiving listener has intercepted >+ * this event. Intercepted events will not be passed to the rest of the >+ * listeners. If the event is not intercepted by any listener, >+ * {@link LayoutAlgorithm#applyLayout() applyLayout()} will be called on the >+ * context's main algorithm.LayoutListener >+ * >+ * @param context >+ * the layout context that fired the event >+ * @param node >+ * the node that has moved >+ * @return true if no further operations after this event are required >+ */ >+ public boolean nodeMoved(LayoutContext context, NodeLayout node); >+ >+ /** >+ * This method is called whenever size of a particular node is changed >+ * within observed context. This usually implicates change of position (the >+ * center of the node) and the receiver should be aware of it (no additional >+ * {@link #nodeMoved(LayoutContext, NodeLayout)} event will be fired). >+ * >+ * If true is returned, it means that the receiving listener has intercepted >+ * this event. Intercepted events will not be passed to the rest of the >+ * listeners. If the event is not intercepted by any listener, >+ * {@link LayoutAlgorithm#applyLayout() applyLayout()} will be called on the >+ * context's main algorithm. >+ * >+ * @param context >+ * the layout context that fired the event >+ * @param node >+ * the node that was resized >+ * @return true if no further operations after this event are required >+ */ >+ public boolean nodeResized(LayoutContext context, NodeLayout node); >+ >+ /** >+ * This method is called whenever location of a particular subgraph is >+ * changed within observed context. If true is returned, it means that the >+ * receiving listener has intercepted this event. Intercepted events will >+ * not be passed to the rest of the listeners. If the event is not >+ * intercepted by any listener, {@link LayoutAlgorithm#applyLayout() >+ * applyLayout()} will be called on the context's main algorithm. >+ * >+ * @param context >+ * the layout context that fired the event >+ * @param subgraph >+ * the subgraph that has moved >+ * @return true if no further operations after this event are required >+ */ >+ public boolean subgraphMoved(LayoutContext context, SubgraphLayout subgraph); >+ >+ /** >+ * This method is called whenever size of a particular subgraph is changed >+ * within observed context. This usually implicates change of position (the >+ * center of the node) and the receiver should be aware of it (no additional >+ * {@link #nodeMoved(LayoutContext, NodeLayout)} event will be fired). >+ * >+ * If true is returned, it means that the receiving listener has intercepted >+ * this event. Intercepted events will not be passed to the rest of the >+ * listeners. If the event is not intercepted by any listener, >+ * {@link LayoutAlgorithm#applyLayout() applyLayout()} will be called on the >+ * context's main algorithm. >+ * >+ * @param context >+ * the layout context that fired the event >+ * @param subgraph >+ * the subgraph that was resized >+ * @return true if no further operations after this event are required >+ */ >+ public boolean subgraphResized(LayoutContext context, SubgraphLayout subgraph); >+ >+} >Index: src/org/eclipse/zest/layouts/interfaces/NodeLayout.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/interfaces/NodeLayout.java >diff -N src/org/eclipse/zest/layouts/interfaces/NodeLayout.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/zest/layouts/interfaces/NodeLayout.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,91 @@ >+/******************************************************************************* >+ * Copyright (c) 2009-2010 Mateusz Matela and others. 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: Mateusz Matela - initial API and implementation >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.layouts.interfaces; >+ >+ >+public interface NodeLayout extends EntityLayout { >+ >+ public boolean isPrunable(); >+ >+ public boolean isPruned(); >+ >+ /** >+ * >+ * @return a subgraph this node belongs to or null if this node is not >+ * pruned >+ */ >+ public SubgraphLayout getSubgraph(); >+ >+ /** >+ * >+ * @param subgraph >+ * a subgraph this node should belong to or null if this node >+ * should not be pruned >+ */ >+ public void prune(SubgraphLayout subgraph); >+ >+ /** >+ * Returns all nodes that are direct successors of this node. Nodes >+ * connected with this node by a bidirectional connection are considered >+ * both successors and predecessors. Any subsequent changes to the returned >+ * array do not affect this node. >+ * >+ * @return array of successors of this node >+ */ >+ public NodeLayout[] getSuccessingNodes(); >+ >+ /** >+ * Returns all nodes that are direct predecessors of this node. Nodes >+ * connected with this node by a bidirectional connection are considered >+ * both successors and predecessors. Any subsequent changes to the returned >+ * array do not affect this node. >+ * >+ * @return array of predecessors of this node >+ */ >+ public NodeLayout[] getPredecessingNodes(); >+ >+ /** >+ * Returns all connections that have this node as a target. All connections >+ * that are bidirectional and are adjacent to this node will be also >+ * included in the result. Any subsequent changes to the returned array do >+ * not affect this node. >+ * >+ * @return array of connections entering this node >+ */ >+ public ConnectionLayout[] getIncomingConnections(); >+ >+ /** >+ * Returns all connections that have this node as a source. All connections >+ * that are bidirectional and are adjacent to this node will be also >+ * included in the result. Any subsequent changes to the returned array do >+ * not affect this node. >+ * >+ * @return array of connections leaving this node >+ */ >+ public ConnectionLayout[] getOutgoingConnections(); >+ >+ /** >+ * Sets the minimized state of this Node. Node that is minimized resizes its >+ * figure to (0, 0). When it is unminimized, it resizes back to previous >+ * dimension. The node's size property is not affected by minimized state, >+ * so an it can be minimized even if it's not resizable. >+ * >+ * @param minimized >+ * new minimized state >+ */ >+ public void setMinimized(boolean minimized); >+ >+ /** >+ * @see #setMinimized(boolean) >+ * >+ * @return true if this entity is minimized >+ */ >+ public boolean isMinimized(); >+} >Index: src/org/eclipse/zest/layouts/interfaces/PruningListener.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/interfaces/PruningListener.java >diff -N src/org/eclipse/zest/layouts/interfaces/PruningListener.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/zest/layouts/interfaces/PruningListener.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,51 @@ >+/******************************************************************************* >+ * Copyright (c) 2009-2010 Mateusz Matela and others. 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: Mateusz Matela - initial API and implementation >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.layouts.interfaces; >+ >+import org.eclipse.zest.layouts.LayoutAlgorithm; >+ >+public interface PruningListener { >+ >+ /** >+ * This method is called when some nodes are pruned in a layout context. >+ * >+ * If true is returned, it means that the receiving listener has intercepted >+ * this event. Intercepted events will not be passed to the rest of the >+ * listeners. If the event is not intercepted by any listener, >+ * {@link LayoutAlgorithm#applyLayout() applyLayout()} will be called on the >+ * context's main algorithm. >+ * >+ * @param context >+ * the layout context that fired the event >+ * @param subgraph >+ * subgraphs that have been created or had nodes added >+ * @return true if no further operations after this event are required >+ */ >+ public boolean nodesPruned(LayoutContext context, SubgraphLayout[] subgraph); >+ >+ /** >+ * This method is called when some nodes are unpruned in a layout context, >+ * that is they are no longer part of a subgraph. >+ * >+ * If true is returned, it means that the receiving listener has intercepted >+ * this event. Intercepted events will not be passed to the rest of the >+ * listeners. If the event is not intercepted by any listener, >+ * {@link LayoutAlgorithm#applyLayout() applyLayout()} will be called on the >+ * context's main algorithm. >+ * >+ * @param context >+ * the layout context that fired the event >+ * @param nodes >+ * nodes that have been unpruned >+ * @return true if no further operations after this event are required >+ */ >+ public boolean nodesUnpruned(LayoutContext context, NodeLayout[] nodes); >+ >+} >Index: src/org/eclipse/zest/layouts/interfaces/SubgraphLayout.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/interfaces/SubgraphLayout.java >diff -N src/org/eclipse/zest/layouts/interfaces/SubgraphLayout.java >--- /dev/null 1 Jan 1970 00:00:00 -0000 >+++ src/org/eclipse/zest/layouts/interfaces/SubgraphLayout.java 1 Jan 1970 00:00:00 -0000 >@@ -0,0 +1,99 @@ >+/******************************************************************************* >+ * Copyright (c) 2009-2010 Mateusz Matela and others. 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: Mateusz Matela - initial API and implementation >+ * Ian Bull >+ ******************************************************************************/ >+package org.eclipse.zest.layouts.interfaces; >+ >+ >+/** >+ * An interface for subgraphs in layout. A subgraph is a set of pruned nodes >+ * that will be displayed as one element. A subgraph must contain at least one >+ * node (empty subgraphs will be removed from its context). Every node can >+ * belong to at most one subgraph. >+ */ >+public interface SubgraphLayout extends EntityLayout { >+ >+ /** >+ * Constant for top-down direction (default). >+ */ >+ public final int TOP_DOWN = 1; >+ >+ /** >+ * Constant for bottom-up direction. >+ */ >+ public final int BOTTOM_UP = 2; >+ >+ /** >+ * Constant for direction from left to right.SubgraphLayout >+ */ >+ public final int LEFT_RIGHT = 3; >+ >+ /** >+ * Constant for direction from right to left. >+ */ >+ public final int RIGHT_LEFT = 4; >+ >+ /** >+ * Returns all the nodes belonging to this subgraph. Replacing elements in >+ * the returned array does not affect this subgraph. >+ * >+ * @return array of nodes >+ */ >+ public NodeLayout[] getNodes(); >+ >+ /** >+ * >+ * @return number of nodes pruned into this subgraph >+ */ >+ public int countNodes(); >+ >+ /** >+ * Adds nodes to this subgraph. If given nodes already belong to another >+ * subgraph, they are first removed from them. >+ * >+ * @param nodes >+ * array of nodes to add >+ */ >+ public void addNodes(NodeLayout[] nodes); >+ >+ /** >+ * Removes nodes from this subgraph. >+ * >+ * @param nodes >+ * array of nodes to remove >+ */ >+ public void removeNodes(NodeLayout[] nodes); >+ >+ /** >+ * Returns true if this subgraph is visualized as a particular object on the >+ * graph. If this method returns false, it means that this subgraph will not >+ * be visible so all methods related to location, size and direction should >+ * be ignored. >+ * >+ * @return whether or not this subgraph is a graph entity that should be >+ * laid out. >+ */ >+ public boolean isGraphEntity(); >+ >+ /** >+ * @return true if this subgraph is visualized differently depending on >+ * direction >+ */ >+ public boolean isDirectionDependant(); >+ >+ /** >+ * Sets the direction of this subgraph (does nothing in case of subgraphs >+ * that don't depend on direction) >+ * >+ * @param direction >+ * one of constants: {@link #TOP_DOWN}, {@link #BOTTOM_UP}, >+ * {@link #LEFT_RIGHT}, {@link #RIGHT_LEFT} >+ */ >+ public void setDirection(int direction); >+ >+} >Index: src/org/eclipse/zest/layouts/progress/ProgressEvent.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/progress/ProgressEvent.java >diff -N src/org/eclipse/zest/layouts/progress/ProgressEvent.java >--- src/org/eclipse/zest/layouts/progress/ProgressEvent.java 12 Sep 2007 20:44:38 -0000 1.3 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,47 +0,0 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts.progress; >- >-/** >- * When an algorithm wants to notify everyone it has completely part of its task, it >- * throws a ProgressEvent. The progress is a number (currentProgress) representing the >- * current steps completed out of the total number of steps (totalProgress) >- * >- * @author Casey Best >- */ >-public class ProgressEvent { >- int stepsCompleted; >- int totalSteps; >- >- /** >- * Creates a progress event. >- * @param stepsCompleted The current progress out of the total >- * @param totalNumberOfSteps The number used to indicate when the algorithm will finish >- */ >- public ProgressEvent (int stepsCompleted, int totalNumberOfSteps) { >- this.stepsCompleted = stepsCompleted; >- this.totalSteps = totalNumberOfSteps; >- } >- >- /** >- * Returns the number of steps already completed. >- */ >- public int getStepsCompleted() { >- return stepsCompleted; >- } >- >- /** >- * Returns the total number of steps to complete. >- */ >- public int getTotalNumberOfSteps() { >- return totalSteps; >- } >-} >Index: src/org/eclipse/zest/layouts/progress/ProgressListener.java >=================================================================== >RCS file: src/org/eclipse/zest/layouts/progress/ProgressListener.java >diff -N src/org/eclipse/zest/layouts/progress/ProgressListener.java >--- src/org/eclipse/zest/layouts/progress/ProgressListener.java 12 Sep 2007 20:44:38 -0000 1.4 >+++ /dev/null 1 Jan 1970 00:00:00 -0000 >@@ -1,38 +0,0 @@ >-/******************************************************************************* >- * Copyright 2005, 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.layouts.progress; >- >- >-/** >- * Listens for ProgressEvents which are thrown by layout algorithms at frequent intervals. >- * >- * @author Ian Bull >- * @author Casey Best >- */ >-public interface ProgressListener { >- >- /** >- * >- * @param e >- */ >- public void progressStarted( ProgressEvent e ); >- >- /** >- * Called when the progress of a layout changes >- */ >- public void progressUpdated (ProgressEvent e); >- >- /** >- * >- * @param e >- */ >- public void progressEnded( ProgressEvent e ); >-}
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 277534
:
145959
| 162001