001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.modules.visual.graph.layout;
042:
043: import org.netbeans.api.visual.graph.layout.GraphLayout;
044: import org.netbeans.api.visual.graph.layout.UniversalGraph;
045: import org.netbeans.api.visual.widget.Widget;
046:
047: import java.awt.*;
048: import java.util.*;
049:
050: /**
051: * @author David Kaspar
052: */
053: public final class TreeGraphLayout<N, E> extends GraphLayout<N, E> {
054:
055: private int originX;
056: private int originY;
057: private int verticalGap;
058: private int horizontalGap;
059: private boolean vertical;
060:
061: private N rootNode;
062:
063: public TreeGraphLayout(int originX, int originY, int verticalGap,
064: int horizontalGap, boolean vertical) {
065: this .originX = originX;
066: this .originY = originY;
067: this .verticalGap = verticalGap;
068: this .horizontalGap = horizontalGap;
069: this .vertical = vertical;
070: }
071:
072: public void setRootNode(N rootNode) {
073: this .rootNode = rootNode;
074: }
075:
076: public void setProperties(int originX, int originY,
077: int verticalGap, int horizontalGap, boolean vertical) {
078: this .originX = originX;
079: this .originY = originY;
080: this .verticalGap = verticalGap;
081: this .horizontalGap = horizontalGap;
082: this .vertical = vertical;
083: }
084:
085: protected void performGraphLayout(UniversalGraph<N, E> graph) {
086: if (rootNode == null)
087: return;
088: Collection<N> allNodes = graph.getNodes();
089: if (!allNodes.contains(rootNode))
090: return;
091: ArrayList<N> nodesToResolve = new ArrayList<N>(allNodes);
092:
093: HashSet<N> loadedSet = new HashSet<N>();
094: Node root = new Node(graph, rootNode, loadedSet);
095: nodesToResolve.removeAll(loadedSet);
096: if (vertical) {
097: root.allocateHorizontally(graph);
098: root.resolveVertically(originX, originY);
099: } else {
100: root.allocateVertically(graph);
101: root.resolveHorizontally(originX, originY);
102: }
103:
104: final HashMap<N, Point> resultPosition = new HashMap<N, Point>();
105: root.upload(resultPosition);
106:
107: for (N node : nodesToResolve) {
108: Point position = new Point();
109: // TODO - resolve others
110: resultPosition.put(node, position);
111: }
112:
113: for (Map.Entry<N, Point> entry : resultPosition.entrySet())
114: setResolvedNodeLocation(graph, entry.getKey(), entry
115: .getValue());
116: }
117:
118: protected void performNodesLayout(
119: UniversalGraph<N, E> universalGraph, Collection<N> nodes) {
120: throw new UnsupportedOperationException(); // TODO
121: }
122:
123: private class Node {
124:
125: private N node;
126: private ArrayList<Node> children;
127:
128: private Rectangle relativeBounds;
129: private int space;
130: private int totalSpace;
131: private Point point;
132:
133: private Node(UniversalGraph<N, E> graph, N node,
134: HashSet<N> loadedSet) {
135: this .node = node;
136: loadedSet.add(node);
137:
138: children = new ArrayList<Node>();
139: for (E edge : graph.findNodeEdges(node, true, false)) {
140: N child = graph.getEdgeTarget(edge);
141: if (child != null && !loadedSet.contains(child))
142: children.add(new Node(graph, child, loadedSet));
143: }
144: }
145:
146: private int allocateHorizontally(UniversalGraph<N, E> graph) {
147: Widget widget = graph.getScene().findWidget(node);
148: widget.getLayout().layout(widget);
149: relativeBounds = widget.getPreferredBounds();
150: space = 0;
151: for (int i = 0; i < children.size(); i++) {
152: if (i > 0)
153: space += horizontalGap;
154: space += children.get(i).allocateHorizontally(graph);
155: }
156: totalSpace = Math.max(space, relativeBounds.width);
157: return totalSpace;
158: }
159:
160: private void resolveVertically(int x, int y) {
161: point = new Point(x + totalSpace / 2, y - relativeBounds.y);
162: x += (totalSpace - space) / 2;
163: y += relativeBounds.height + verticalGap;
164: for (Node child : children) {
165: child.resolveVertically(x, y);
166: x += child.totalSpace + horizontalGap;
167: }
168: }
169:
170: private int allocateVertically(UniversalGraph<N, E> graph) {
171: Widget widget = graph.getScene().findWidget(node);
172: widget.getLayout().layout(widget);
173: relativeBounds = widget.getPreferredBounds();
174: space = 0;
175: for (int i = 0; i < children.size(); i++) {
176: if (i > 0)
177: space += verticalGap;
178: space += children.get(i).allocateVertically(graph);
179: }
180: totalSpace = Math.max(space, relativeBounds.height);
181: return totalSpace;
182: }
183:
184: private void resolveHorizontally(int x, int y) {
185: point = new Point(x - relativeBounds.x, y + totalSpace / 2);
186: x += relativeBounds.width + horizontalGap;
187: y += (totalSpace - space) / 2;
188: for (Node child : children) {
189: child.resolveHorizontally(x, y);
190: y += child.totalSpace + verticalGap;
191: }
192: }
193:
194: private void upload(HashMap<N, Point> result) {
195: result.put(node, point);
196: for (Node child : children)
197: child.upload(result);
198: }
199: }
200:
201: }
|