diff --git a/api.visual/apichanges.xml b/api.visual/apichanges.xml
--- a/api.visual/apichanges.xml
+++ b/api.visual/apichanges.xml
@@ -757,6 +757,21 @@
+
+
+ Support for rotating widgets
+
+
+
+
+
+ Added support for rotating widgets. Patch provided by user
+ bcallebaut.
+
+
+
+
+
diff --git a/api.visual/manifest.mf b/api.visual/manifest.mf
--- a/api.visual/manifest.mf
+++ b/api.visual/manifest.mf
@@ -1,6 +1,6 @@
Manifest-Version: 1.0
OpenIDE-Module: org.netbeans.api.visual
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/visual/resources/Bundle.properties
-OpenIDE-Module-Specification-Version: 2.34
+OpenIDE-Module-Specification-Version: 2.35
AutoUpdate-Essential-Module: true
diff --git a/api.visual/src/org/netbeans/api/visual/action/WidgetAction.java b/api.visual/src/org/netbeans/api/visual/action/WidgetAction.java
--- a/api.visual/src/org/netbeans/api/visual/action/WidgetAction.java
+++ b/api.visual/src/org/netbeans/api/visual/action/WidgetAction.java
@@ -1087,6 +1087,83 @@
}
/**
+ * Utility class that provides a Chain object along with its name to the
+ * widget.
+ *
+ * @since 2.35
+ * @author bcallebaut
+ */
+ public static final class ChainProvider {
+
+ private String name = ""; //NOI18N
+
+ /**
+ * Get the chain name
+ *
+ * @return the chain name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Set the chain name
+ *
+ * @param name new chain name
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+ private Chain chain;
+
+ /**
+ * Get the chain object provided by this provider
+ *
+ * @return the value of chain
+ */
+ public Chain getChain() {
+ return chain;
+ }
+
+ /**
+ * Set the chain provided by this provider
+ *
+ * @param chain new value of chain
+ */
+ public void setChain(Chain chain) {
+ this.chain = chain;
+ }
+
+ /**
+ * Constructor without parameter. Creates a non initialized provider
+ * instance. Set the chain name and the Chain instance before using it.
+ */
+ public ChainProvider() {
+ }
+
+ /**
+ * Creates a non fully initialized provider instance. Set the chain name
+ * before using it.
+ *
+ * @param chain Chain instance provided by this provider
+ */
+ public ChainProvider(Chain chain) {
+ this.chain = chain;
+ }
+
+ /**
+ * Creates a fully initialized Chain provider instance.
+ *
+ * @param name name of the chain
+ * @param chain chain instance
+ */
+ public ChainProvider(String name, Chain chain) {
+ this.name = name;
+ this.chain = chain;
+ }
+ }
+
+ /**
* Represents an widget event.
*/
public static interface WidgetEvent {
diff --git a/api.visual/src/org/netbeans/api/visual/widget/Scene.java b/api.visual/src/org/netbeans/api/visual/widget/Scene.java
--- a/api.visual/src/org/netbeans/api/visual/widget/Scene.java
+++ b/api.visual/src/org/netbeans/api/visual/widget/Scene.java
@@ -56,6 +56,7 @@
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
import java.awt.*;
+import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.HashSet;
@@ -406,7 +407,7 @@
private void resolveRepaints () {
for (Widget widget : repaintWidgets) {
- Rectangle repaintBounds = widget.getBounds ();
+ Rectangle repaintBounds = getRealBounds(widget);
if (repaintBounds == null)
continue;
repaintBounds = widget.convertLocalToScene (repaintBounds);
@@ -684,4 +685,23 @@
}
+ /**
+ * Compute outer bounds sufficient even if the object is rotated. It is
+ * needed to repaint areas that were uncovered by rotating the widget. It is
+ * done by computing enclosing circle and getting its bounds.
+ */
+ private Rectangle getRealBounds(Widget w) {
+ if (w.getTransform() == null || w.getShape() == null) {
+ return w.getBounds();
+ } else {
+ Rectangle bounds = w.getShape().getBounds();
+ int a = (int) bounds.getWidth() / 2; // half of width
+ int b = (int) bounds.getHeight() / 2; // half of height
+ int mX = (int) (bounds.getX() + a); // middle X
+ int mY = (int) (bounds.getY() + b); // middle Y
+ double radius = Math.sqrt(a * a + b * b);
+ return new Ellipse2D.Double(mX - radius, mY - radius,
+ 2 * radius, 2 * radius).getBounds();
+ }
+ }
}
diff --git a/api.visual/src/org/netbeans/api/visual/widget/Widget.java b/api.visual/src/org/netbeans/api/visual/widget/Widget.java
--- a/api.visual/src/org/netbeans/api/visual/widget/Widget.java
+++ b/api.visual/src/org/netbeans/api/visual/widget/Widget.java
@@ -89,11 +89,14 @@
* and actions. Yherefore you can define your own look and feel directly in the that method.
*
* Since version 2.6 Widget class implements Accessible interface.
+ * Since version 2.35 Widget class implements Lookup.Provider interface and
+ * tries to initialize itself using the lookup passed in the constructor.
*
* @author David Kaspar
+ * @author Benoit Callebaut
*/
// TODO - Should Widget be an abstract class?
-public class Widget implements Accessible {
+public class Widget implements Accessible, Lookup.Provider {
static final String MESSAGE_NULL_BOUNDS = "Scene.validate was not called after last change. Widget is not validated. See first Q/A at http://graph.netbeans.org/faq.html page.";
@@ -145,6 +148,7 @@
private Point location;
private Rectangle bounds;
+ private Shape shape;
private Rectangle calculatedPreferredBounds;
private boolean requiresFullValidation;
@@ -158,26 +162,44 @@
* a property is set by using using resources.
*/
private ResourceTable resourceTable = null;
+ private final Lookup lookup;
+ private AffineTransform transform = new AffineTransform();
/**
* Creates a new widget which will be used in a specified scene.
* @param scene the scene where the widget is going to be used
+ * @since 2.35
*/
- public Widget (Scene scene) {
- if (scene == null)
+ public Widget (Scene scene, Lookup lookup) {
+ if (scene == null) {
scene = (Scene) this;
+ }
this.scene = scene;
+ this.lookup = lookup;
children = new ArrayList ();
childrenUm = Collections.unmodifiableList (children);
actionsChain = new WidgetAction.Chain ();
+ toolsActions = new HashMap();
+ toolsActions.put(null, actionsChain);
+
+ for (WidgetAction.ChainProvider provider : lookup.lookupAll(WidgetAction.ChainProvider.class)) {
+ toolsActions.put(provider.getName(), provider.getChain());
+ }
opaque = false;
font = null;
background = Color.WHITE;
foreground = Color.BLACK;
- border = BorderFactory.createEmptyBorder ();
- layout = LayoutFactory.createAbsoluteLayout ();
+
+ border = lookup.lookup(Border.class);
+ if (border == null) {
+ border = BorderFactory.createEmptyBorder ();
+ }
+ layout = lookup.lookup(Layout.class);
+ if (layout == null) {
+ layout = LayoutFactory.createAbsoluteLayout ();
+ }
preferredLocation = null;
preferredBounds = null;
checkClipping = false;
@@ -191,6 +213,24 @@
}
/**
+ * Creates a new widget which will be used in a specified scene.
+ *
+ * @param scene the scene where the widget is going to be used
+ * @since 2.35
+ */
+ public Widget(Scene scene) {
+ this(scene, Lookup.EMPTY);
+ }
+
+ /**
+ * @since 2.35
+ */
+ public Shape getShape() {
+ shape = getTransform().createTransformedShape (getBounds());
+ return shape;
+ }
+
+ /**
* Returns a scene where the widget is assigned
* @return the scene
*/
@@ -493,8 +533,9 @@
* Returns a lookup of the widget.
* @return the lookup
*/
- public Lookup getLookup () {
- return Lookup.EMPTY;
+ @Override
+ public Lookup getLookup() {
+ return lookup;
}
/**
@@ -840,6 +881,22 @@
}
/**
+ * @since 2.35
+ * @return Widget transform
+ */
+ public AffineTransform getTransform() {
+ return transform;
+ }
+
+ /**
+ * @since 2.35
+ */
+ public void setTransform(AffineTransform transform) {
+ this.transform = transform;
+ this.revalidate();
+ }
+
+ /**
* Returns the border of the widget.
* @return the border
*/
@@ -1276,7 +1333,7 @@
* @return true, if the location belong to the widget
*/
public boolean isHitAt (Point localLocation) {
- return visible && getBounds ().contains (localLocation);
+ return visible && getShape().contains (localLocation);
}
/**
@@ -1399,10 +1456,11 @@
return;
assert bounds != null : MESSAGE_NULL_BOUNDS; // NOI18N
- Graphics2D gr = scene.getGraphics ();
+ Graphics2D gr = getGraphics ();
AffineTransform previousTransform = gr.getTransform();
gr.translate (location.x, location.y);
+ gr.transform (transform);
Shape tempClip = null;
if (checkClipping) {
diff --git a/api.visual/src/org/netbeans/modules/visual/graph/layout/HierarchicalLayout.java b/api.visual/src/org/netbeans/modules/visual/graph/layout/HierarchicalLayout.java
--- a/api.visual/src/org/netbeans/modules/visual/graph/layout/HierarchicalLayout.java
+++ b/api.visual/src/org/netbeans/modules/visual/graph/layout/HierarchicalLayout.java
@@ -659,7 +659,7 @@
protected void run() {
- layers = new List[layerCount];
+ layers = (List.LayoutNode>[])new List[layerCount];
for (int i = 0; i < layerCount; i++) {
layers[i] = new ArrayList();