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.layout;
042:
043: import org.netbeans.modules.visual.util.GeomUtil;
044: import org.netbeans.api.visual.widget.ConnectionWidget;
045: import org.netbeans.api.visual.widget.Widget;
046: import org.netbeans.api.visual.layout.Layout;
047: import org.netbeans.api.visual.layout.LayoutFactory;
048:
049: import java.awt.*;
050: import java.util.HashMap;
051:
052: /**
053: * @author David Kaspar
054: */
055: public final class ConnectionWidgetLayout implements Layout {
056:
057: private ConnectionWidget connectionWidget;
058: private HashMap<Widget, LayoutFactory.ConnectionWidgetLayoutAlignment> alignments;
059: private HashMap<Widget, Float> percentagePlacements;
060: private HashMap<Widget, Integer> distancePlacements;
061:
062: public ConnectionWidgetLayout(ConnectionWidget connectionWidget) {
063: this .connectionWidget = connectionWidget;
064: }
065:
066: public void setConstraint(Widget childWidget,
067: LayoutFactory.ConnectionWidgetLayoutAlignment alignment,
068: float placementInPercentage) {
069: assert childWidget != null;
070: assert alignment != null;
071:
072: if (alignments == null)
073: alignments = new HashMap<Widget, LayoutFactory.ConnectionWidgetLayoutAlignment>();
074: alignments.put(childWidget, alignment);
075:
076: if (percentagePlacements == null)
077: percentagePlacements = new HashMap<Widget, Float>();
078: percentagePlacements.put(childWidget, placementInPercentage);
079:
080: if (distancePlacements != null)
081: distancePlacements.remove(childWidget);
082: }
083:
084: public void setConstraint(Widget childWidget,
085: LayoutFactory.ConnectionWidgetLayoutAlignment alignment,
086: int placementAtDistance) {
087: assert childWidget != null;
088: assert alignment != null;
089:
090: if (alignments == null)
091: alignments = new HashMap<Widget, LayoutFactory.ConnectionWidgetLayoutAlignment>();
092: alignments.put(childWidget, alignment);
093:
094: if (percentagePlacements != null)
095: percentagePlacements.remove(childWidget);
096:
097: if (distancePlacements == null)
098: distancePlacements = new HashMap<Widget, Integer>();
099: distancePlacements.put(childWidget, placementAtDistance);
100: }
101:
102: public void removeConstraint(Widget childWidget) {
103: assert childWidget != null;
104:
105: if (alignments != null)
106: alignments.remove(childWidget);
107: if (percentagePlacements != null)
108: percentagePlacements.remove(childWidget);
109: if (distancePlacements != null)
110: distancePlacements.remove(childWidget);
111: }
112:
113: public void layout(Widget widget) {
114: assert connectionWidget == widget;
115:
116: connectionWidget.calculateRouting();
117: java.util.List<Point> controlPoints = connectionWidget
118: .getControlPoints();
119: boolean empty = controlPoints == null
120: || controlPoints.size() <= 0;
121:
122: double totalDistance = 0.0;
123: double[] distances = new double[empty ? 0 : controlPoints
124: .size() - 1];
125: for (int i = 0; i < distances.length; i++)
126: distances[i] = totalDistance += GeomUtil.distanceSq(
127: controlPoints.get(i), controlPoints.get(i + 1));
128:
129: for (Widget child : widget.getChildren()) {
130: Float percentage = percentagePlacements != null ? percentagePlacements
131: .get(child)
132: : null;
133: Integer distance = distancePlacements != null ? distancePlacements
134: .get(child)
135: : null;
136:
137: if (empty)
138: layoutChildAt(child, new Point());
139: else if (percentage != null) {
140: if (percentage <= 0.0)
141: layoutChildAt(child, connectionWidget
142: .getFirstControlPoint());
143: else if (percentage >= 1.0)
144: layoutChildAt(child, connectionWidget
145: .getLastControlPoint());
146: else
147: layoutChildAtDistance(distances,
148: (int) (percentage * totalDistance), child,
149: controlPoints);
150: } else if (distance != null) {
151: if (distance < 0)
152: layoutChildAtDistance(distances, distance
153: + (int) totalDistance, child, controlPoints);
154: else
155: layoutChildAtDistance(distances, distance, child,
156: controlPoints);
157: } else
158: layoutChildAt(child, new Point());
159: }
160: }
161:
162: public boolean requiresJustification(Widget widget) {
163: return false;
164: }
165:
166: public void justify(Widget widget) {
167: }
168:
169: private void layoutChildAtDistance(double[] distances,
170: int lineDistance, Widget child,
171: java.util.List<Point> controlPoints) {
172: int index = distances.length - 1;
173: for (int i = 0; i < distances.length; i++) {
174: if (lineDistance < distances[i]) {
175: index = i;
176: break;
177: }
178: }
179:
180: double segmentStartDistance = index > 0 ? distances[index - 1]
181: : 0;
182: double segmentLength = distances[index] - segmentStartDistance;
183: double segmentDistance = lineDistance - segmentStartDistance;
184:
185: if (segmentLength == 0.0) {
186: layoutChildAt(child, controlPoints.get(index));
187: return;
188: }
189:
190: Point p1 = controlPoints.get(index);
191: Point p2 = controlPoints.get(index + 1);
192:
193: double segmentFactor = segmentDistance / segmentLength;
194:
195: layoutChildAt(child, new Point((int) (p1.x + (p2.x - p1.x)
196: * segmentFactor), (int) (p1.y + (p2.y - p1.y)
197: * segmentFactor)));
198: }
199:
200: private void layoutChildAt(Widget childWidget, Point linePoint) {
201: if (!childWidget.isVisible()) {
202: childWidget.resolveBounds(new Point(linePoint.x,
203: linePoint.y), new Rectangle());
204: return;
205: }
206: Rectangle preferredBounds = childWidget.getPreferredBounds();
207: LayoutFactory.ConnectionWidgetLayoutAlignment alignment = null;
208: if (alignments != null)
209: alignment = alignments.get(childWidget);
210: if (alignment == null)
211: alignment = LayoutFactory.ConnectionWidgetLayoutAlignment.NONE;
212: Point referencePoint = getReferencePoint(alignment,
213: preferredBounds);
214: Point location = childWidget.getPreferredLocation();
215: if (location != null)
216: referencePoint.translate(-location.x, -location.y);
217: childWidget.resolveBounds(new Point(linePoint.x
218: - referencePoint.x, linePoint.y - referencePoint.y),
219: preferredBounds);
220: }
221:
222: private static Point getReferencePoint(
223: LayoutFactory.ConnectionWidgetLayoutAlignment alignment,
224: Rectangle rectangle) {
225: switch (alignment) {
226: case BOTTOM_CENTER:
227: return new Point(GeomUtil.centerX(rectangle),
228: rectangle.y - 1);
229: case BOTTOM_LEFT:
230: return new Point(rectangle.x + rectangle.width,
231: rectangle.y - 1);
232: case BOTTOM_RIGHT:
233: return new Point(rectangle.x - 1, rectangle.y - 1);
234: case CENTER:
235: return GeomUtil.center(rectangle);
236: case CENTER_LEFT:
237: return new Point(rectangle.x + rectangle.width, GeomUtil
238: .centerY(rectangle));
239: case CENTER_RIGHT:
240: return new Point(rectangle.x - 1, GeomUtil
241: .centerY(rectangle));
242: case NONE:
243: return new Point();
244: case TOP_CENTER:
245: return new Point(GeomUtil.centerX(rectangle), rectangle.y
246: + rectangle.height);
247: case TOP_LEFT:
248: return new Point(rectangle.x + rectangle.width, rectangle.y
249: + rectangle.height);
250: case TOP_RIGHT:
251: return new Point(rectangle.x - 1, rectangle.y
252: + rectangle.height);
253: default:
254: return new Point();
255: }
256: }
257:
258: }
|