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.action;
042:
043: import org.netbeans.api.visual.anchor.Anchor;
044: import org.netbeans.api.visual.widget.ConnectionWidget;
045: import org.netbeans.api.visual.widget.Scene;
046: import org.netbeans.api.visual.widget.Widget;
047: import org.netbeans.api.visual.action.*;
048:
049: import java.awt.*;
050: import java.awt.event.MouseEvent;
051: import java.awt.event.KeyEvent;
052: import java.util.List;
053:
054: /**
055: * Override at least isReplacementWidget and setConnectionAnchor methods. reconnectingFinished is always called before setConnectionAnchor.
056: *
057: * @author David Kaspar
058: */
059: public final class ReconnectAction extends WidgetAction.LockedAdapter {
060:
061: private static final int MIN_DIFFERENCE = 5;
062:
063: private ReconnectDecorator decorator;
064: private ReconnectProvider provider;
065:
066: private ConnectionWidget connectionWidget = null;
067: private boolean reconnectingSource = false;
068: private Point floatPoint = null;
069: private Widget replacementWidget = null;
070: private Anchor originalAnchor = null;
071:
072: public ReconnectAction(ReconnectDecorator decorator,
073: ReconnectProvider provider) {
074: this .decorator = decorator;
075: this .provider = provider;
076: }
077:
078: protected boolean isLocked() {
079: return connectionWidget != null;
080: }
081:
082: public State mousePressed(Widget widget, WidgetMouseEvent event) {
083: if (isLocked())
084: return State.createLocked(widget, this );
085: if (event.getButton() == MouseEvent.BUTTON1
086: && event.getClickCount() == 1) {
087: if (widget instanceof ConnectionWidget) {
088: ConnectionWidget conn = (ConnectionWidget) widget;
089: int index = conn.getControlPointHitAt(event.getPoint());
090: List<Point> controlPoints = conn.getControlPoints();
091: if (index == 0 && provider.isSourceReconnectable(conn)) {
092: reconnectingSource = true;
093: } else if (controlPoints != null
094: && index == controlPoints.size() - 1
095: && provider.isTargetReconnectable(conn)) {
096: reconnectingSource = false;
097: } else {
098: return State.REJECTED;
099: }
100:
101: floatPoint = new Point(event.getPoint());
102: replacementWidget = null;
103: connectionWidget = conn;
104: provider.reconnectingStarted(conn, reconnectingSource);
105: if (reconnectingSource)
106: originalAnchor = connectionWidget.getSourceAnchor();
107: else
108: originalAnchor = connectionWidget.getTargetAnchor();
109: return State.createLocked(widget, this );
110: }
111: }
112: return State.REJECTED;
113: }
114:
115: public State mouseReleased(Widget widget, WidgetMouseEvent event) {
116: Point point = event.getPoint();
117: boolean state = move(widget, point);
118: if (state) {
119: cancel(point);
120: }
121: return state ? State.CONSUMED : State.REJECTED;
122: }
123:
124: private void cancel(Point point) {
125: if (reconnectingSource)
126: connectionWidget.setSourceAnchor(originalAnchor);
127: else
128: connectionWidget.setTargetAnchor(originalAnchor);
129: provider.reconnectingFinished(connectionWidget,
130: reconnectingSource);
131: if (point != null)
132: if (Math.abs(floatPoint.x - point.x) >= ReconnectAction.MIN_DIFFERENCE
133: || Math.abs(floatPoint.y - point.y) >= ReconnectAction.MIN_DIFFERENCE)
134: provider.reconnect(connectionWidget, replacementWidget,
135: reconnectingSource);
136: replacementWidget = null;
137: floatPoint = null;
138: connectionWidget = null;
139: }
140:
141: public State mouseDragged(Widget widget, WidgetMouseEvent event) {
142: return move(widget, event.getPoint()) ? State.createLocked(
143: widget, this ) : State.REJECTED;
144: }
145:
146: private boolean move(Widget widget, Point point) {
147: if (connectionWidget != widget)
148: return false;
149:
150: Point replacementSceneLocation = widget
151: .convertLocalToScene(point);
152: replacementWidget = resolveReplacementWidgetCore(
153: connectionWidget.getScene(), replacementSceneLocation);
154: Anchor replacementAnchor = null;
155: if (replacementWidget != null)
156: replacementAnchor = decorator
157: .createReplacementWidgetAnchor(replacementWidget);
158: if (replacementAnchor == null)
159: replacementAnchor = decorator
160: .createFloatAnchor(replacementSceneLocation);
161:
162: if (reconnectingSource)
163: connectionWidget.setSourceAnchor(replacementAnchor);
164: else
165: connectionWidget.setTargetAnchor(replacementAnchor);
166:
167: return true;
168: }
169:
170: protected Widget resolveReplacementWidgetCore(Scene scene,
171: Point sceneLocation) {
172: if (provider != null)
173: if (provider.hasCustomReplacementWidgetResolver(scene))
174: return provider.resolveReplacementWidget(scene,
175: sceneLocation);
176: Point sceneOrigin = scene.getLocation();
177: sceneLocation = new Point(sceneLocation.x + sceneOrigin.x,
178: sceneLocation.y + sceneOrigin.y);
179: Widget[] result = new Widget[] { null };
180: resolveReplacementWidgetCoreDive(result, scene, sceneLocation);
181: return result[0];
182: }
183:
184: private boolean resolveReplacementWidgetCoreDive(Widget[] result,
185: Widget widget, Point parentLocation) {
186: if (widget == connectionWidget)
187: return false;
188:
189: Point widgetLocation = widget.getLocation();
190: Point location = new Point(parentLocation.x - widgetLocation.x,
191: parentLocation.y - widgetLocation.y);
192:
193: if (!widget.getBounds().contains(location))
194: return false;
195:
196: java.util.List<Widget> children = widget.getChildren();
197: for (int i = children.size() - 1; i >= 0; i--) {
198: if (resolveReplacementWidgetCoreDive(result, children
199: .get(i), location))
200: return true;
201: }
202:
203: if (!widget.isHitAt(location))
204: return false;
205:
206: ConnectorState state = provider.isReplacementWidget(
207: connectionWidget, widget, reconnectingSource);
208: if (state == ConnectorState.REJECT)
209: return false;
210: if (state == ConnectorState.ACCEPT)
211: result[0] = widget;
212: return true;
213: }
214:
215: public State keyPressed(Widget widget, WidgetKeyEvent event) {
216: if (isLocked() && event.getKeyCode() == KeyEvent.VK_ESCAPE) {
217: cancel(null);
218: return State.CONSUMED;
219: }
220: return super.keyPressed(widget, event);
221: }
222:
223: }
|