blob: e5e093369518fc07708cecc58a5d113dfca80f7c [file] [log] [blame]
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<!-- -*- xhtml -*- -->
<title>NetBeans Visual Library Tutorial for NetBeans Platform 7.1</title>
<link rel="stylesheet" type="text/css" href="https://netbeans.org/netbeans.css"/>
<meta name="AUDIENCE" content="NBUSER"/>
<meta name="TYPE" content="ARTICLE"/>
<meta name="EXPIRES" content="N"/>
<meta name="developer" content="geertjan.wielenga@sun.com"/>
<meta name="indexed" content="y"/>
<meta name="description" content="A walk-through of the basic features of the Visual Library API."/>
<!-- Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. -->
<!-- Use is subject to license terms.-->
</head>
<body>
<h1>NetBeans Visual Library Tutorial</h1>
<p>In this tutorial, you will learn how to use the main
features provided by the <a href="http://bits.netbeans.org/dev/javadoc/org-netbeans-api-visual/overview-summary.html">NetBeans Visual Library API</a>.
The library provides a set of reusable pieces, called "widgets".
When you compose them together, you are able to create a visualization
in a simple and flexible way.
Each widget has, built into it, various properties, such as "layout", "border",
and "actions". The set of predefined widgets provided by the library can be extended.</p>
<p>All the pluggable pieces are declared as interfaces or abstract classes, such
as "WidgetAction", "Anchor", "AnchorShape", "PointShape", "Animator", "Border",
"GraphLayout", "LookFeel", "Layout", "SceneLayout", "Router", and "CollisionsCollector".</p>
<p>In summary, the Visual Library API
is a visualization API, useful in the context of modeling and graphing features.</p>
<p><strong class="notes">Note: </strong>This document uses
NetBeans Platform 7.1 and NetBeans IDE 7.1. If you
are using an earlier version, see <a href="71/nbm-visual_library.html">the previous version
of this document</a>.</p>
<p><b>Contents</b></p>
<p><img src="../../images/articles/72/netbeans-stamp-71-72.gif" class="stamp" width="114" height="114" alt="Content on this page applies to NetBeans IDE 7.1" title="Content on this page applies to NetBeans IDE 7.1"/></p>
<ul class="toc">
<li><a href="#getting-started">Setting Up the Module</a></li>
<li><a href="#creating-the-scene">Creating the Scene</a></li>
<li><a href="#creating-a-palette">Creating a Component Palette for the Scene</a></li>
<li><a href="#adding-a-layer-widget">Adding a LayerWidget</a></li>
<li><a href="#adding-an-icon-widget">Adding an IconNodeWidget via Drag and Drop Functionality</a></li>
<li><a href="#adding-to-scene">Adding Functionality to the Scene</a></li>
<li><a href="#adding-to-widget">Adding Functionality to the IconNodeWidget</a></li>
</ul>
<p><b>To follow this tutorial, you need the software and resources listed in the following
table.</b></p>
<table>
<tbody>
<tr>
<th class="tblheader" scope="col">Software or Resource</th>
<th class="tblheader" scope="col">Version Required</th>
</tr>
<tr>
<td class="tbltd1"><a href="https://netbeans.org/downloads/index.html">NetBeans IDE</a></td>
<td class="tbltd1">version 7.1 or above</td>
</tr>
<tr>
<td class="tbltd1"><a href="http://java.sun.com/javase/downloads/index.jsp">Java Developer Kit (JDK)</a></td>
<td class="tbltd1">version 6 or above</td>
</tr>
</tbody>
</table>
<p>Also, you will use 3 icons in the tutorial. You can right-click
them here and save them locally, then copy them to the module project's
location, after you create the module project later in this tutorial. Here are
the 3 icons:</p>
<p><img alt="" src="../../images/tutorials/vislib/image1.png"/>
<img alt="" src="../../images/tutorials/vislib/image2.png"/>
<img alt="" src="../../images/tutorials/vislib/image3.png"/></p>
<p>All the information you need to know for working with the Visual Library API
is collected at these two locations:</p>
<ul>
<li><a href="http://graph.netbeans.org/">Visual Library Project Page</a></li>
<li><a href="http://graph.netbeans.org/documentation.html">Visual Library 2.0 - Documentation</a></li>
</ul>
<!-- ===================================================================================== -->
<h2><a name="getting-started"></a>Setting Up the Module</h2>
<p>In this section, we use wizards to create a module project and
a custom window component.</p>
<ol>
<li>Choose File &gt; New Project (Ctrl+Shift+N). Under Categories, select NetBeans Modules.
Under Projects, select Module. Click Next.</li>
<li>In the Name and Location panel, type <tt>ShapeSample</tt> in the Project Name field.
Change the Project Location to any directory on your computer. Leave the Standalone Module option
and Set as Main Project checkbox selected. Click Next.</li>
<li><p>In the Basic Module Configuration panel, type <tt>org.netbeans.shapesample</tt>
in Code Name Base. Click Finish.</p>
<p>The IDE creates the <tt>ShapeSample</tt> project. The project contains all of your sources and project
metadata, such as the project's Ant build script. The project opens in the IDE. You can view its logical
structure in the Projects window (Ctrl+1) and its file structure in the Files window (Ctrl+2).
</p></li>
<li>Right-click the module project, choose
New &gt; Other and choose Window from the
Module Development category. Click Next. Choose <tt>editor</tt>
in the drop-down list and select Open on Application Start. Click Next.</li>
<li><p>Type <tt>Shape</tt> in Class Name Prefix. Optionally,
add an icon with a dimension of 16x16 pixels. Click Finish.</p>
<p>A Java class is created, named <tt>ShapeTopComponent</tt>.
Open this file in "Design" mode. You should now see this:</p>
<p><img alt="" style="border:1px solid black" src="../../images/tutorials/vislib/71/shapetopcomponent.png"/></p>
</li>
<li><p>Right-click the project, choose Properties, click Libraries
in the Project Properties dialog box and declare a dependency on the Visual Library API. Click OK.</p>
</li>
</ol>
<h2><a name="creating-the-scene"></a>Creating the Scene</h2>
<p>Programming with the Visual Library API is similar to programming in Swing. You build
and modify a tree of visual elements that are called "widgets". The root of the tree is
represented by a Scene class which holds all the visual data of the scene. The scene is a widget.
You have to create a scene view, which is a JComponent. You must then add
the JComponent to a JScrollPane.</p>
<p>In this section,
we add a JScrollPane to our TopComponent. Then we create a new
scene. Next, we pass the scene view to the TopComponent, so that it can be displayed in
the TopComponent's JScrollPane. We then install the module project and display
our first scene.</p>
<ol>
<li><p>In the <tt>org.netbeans.shapesample</tt> package, create a Java class called <tt>GraphSceneImpl</tt>.
Let the class extend <tt>GraphScene</tt>.</p>
<p>A red error underline and a lightbulb appears. Let the IDE generate the import statement.</p>
<p>A red error underline and a lightbulb appears again. Let the IDE generate the class's abstract methods.</p>
</li>
<li>Create a dummy implementation of the class by adding
meaningful identifiers and setting <tt>null</tt> to be returned where necessary,
so that all of the class's requirements are fulfilled:
<pre class="examplecode">public class GraphSceneImpl extends GraphScene {
public GraphSceneImpl() {
}
@Override
protected Widget attachNodeWidget(Object node) {
return null;
}
@Override
protected Widget attachEdgeWidget(Object edge) {
return null;
}
@Override
protected void attachEdgeSourceAnchor(Object edge, Object oldSourceNode, Object newSourceNode) {
}
@Override
protected void attachEdgeTargetAnchor(Object edge, Object oldTargetNode, Object newTargetNode) {
}
}</pre></li>
<li>Now use the TopComponent's
constructor to hold an instance of the <tt>GraphSceneImpl</tt> class. To do so, add
the following to the end of the <tt>ShapeTopComponent</tt> class's constructor:
<pre class="examplecode">setLayout(new BorderLayout());
GraphSceneImpl scene = new GraphSceneImpl();
JScrollPane shapePane = new JScrollPane();
shapePane.setViewportView(scene.createView());
add(shapePane, BorderLayout.CENTER);
add(scene.createSatelliteView(), BorderLayout.WEST);</pre>
<p>Notice that we are creating two views. The first will be a large view for
visualization of your graphs or models, and so on. The second is a satellite view,
which we have placed in the WEST (i.e., the left side) of the TopComponent. This will
let the user navigate quickly across the view and gives an overview of the entire scene.</p>
</li>
<li>Right-click the project node and choose "Run".</li>
</ol>
<p>A new instance of the application starts up.
When the module installs, look under the Window menu and you will find a new menu item
called "Shape", at the top of the list of menu items.
Choose it and you will see the start of your Visual Library API implementation:</p>
<p><img alt="" src="../../images/tutorials/vislib/firstscene.png"/></p>
<!-- ===================================================================================== -->
<h2><a name="creating-a-palette"></a>Creating a Component Palette for the Scene</h2>
<p>To do something useful with the Visual Library API, we will implement the <a href="http://bits.netbeans.org/dev/javadoc/org-netbeans-spi-palette/overview-summary.html">Palette API</a>
so that we end up with a Component Palette containing the shapes shown at the start of this
tutorial. Later, we will add the Visual Library API's drag and drop functionality so that
we can drag and drop the shapes
into the scene. After that, we will be able to enrich the scene with additional features,
such as the ability to zoom and pan in the scene.</p>
<ol>
<li><p>Since the focus of this tutorial is the Visual Library API, and not the Palette API,
no time will be spent here explaining how the Palette API works. Many tutorials
exist on this subject already (<a href="https://netbeans.org/kb/trails/platform.html">here</a>).
Therefore, you can simply copy and paste the following files into a new package
called <tt>org.netbeans.shapesample.palette</tt>:</p>
<ul>
<li><a href="../images/tutorials/vislib/Category.java">Category.java</a></li>
<li><a href="../images/tutorials/vislib/CategoryChildren.java">CategoryChildren.java</a></li>
<li><a href="../images/tutorials/vislib/CategoryNode.java">CategoryNode.java</a></li>
<li><a href="../images/tutorials/vislib/PaletteSupport.java">PaletteSupport.java</a></li>
<li><a href="../images/tutorials/vislib/Shape.java">Shape.java</a></li>
<li><a href="../images/tutorials/vislib/ShapeChildren.java">ShapeChildren.java</a></li>
<li><a href="../images/tutorials/vislib/ShapeNode.java">ShapeNode.java</a></li>
</ul>
</li>
<li>In the same way as explained in step 3 of the section called "Getting Started", earlier in this tutorial,
add dependencies on the Actions API, Nodes API, and Common Palette API.</li>
<li>Next, add the palette to the TopComponent's Lookup, by adding this
line to the end of the TopComponent's constructor:
<pre class="examplecode">associateLookup( Lookups.singleton(PaletteSupport.createPalette() ));</pre>
</li>
<li>The IDE will prompt you to insert import statements for <tt>org.openide.util.lookup.Lookups</tt>
and <tt>org.netbeans.shapesample.palette.PaletteSupport</tt>. Accept the prompts and let
the IDE generate the import statements.</li>
<li><p>Place the images found at the start of this tutorial
into the <tt>org.netbeans.shapesample.palette</tt> package.
The Projects window should now look as follows:</p>
<p><img alt="" src="../../images/tutorials/vislib/71/proj-window.png"/></p></li>
<li><p>Install the module again. The new
Component Palette is shown to the right of the scene:</p>
<p><img alt="" src="../../images/tutorials/vislib/firstpalette.png"/></p>
</li>
</ol>
<p>When you try to drag and drop a widget onto
the scene, nothing happens because you need
a <tt>LayerWidget</tt> on which you will be
able to drop your widgets. You will
be shown how to do so in the next section.</p>
<!-- ===================================================================================== -->
<h2><a name="adding-a-layer-widget"></a>Adding a LayerWidget</h2>
<p>A <a href="http://bits.netbeans.org/dev/javadoc/org-netbeans-api-visual/org/netbeans/api/visual/widget/LayerWidget.html">LayerWidget</a>
represents a glasspane, similar to JGlassPane in Swing. It is transparent by default. So,
before we go any further, we will add a LayerWidget to the scene, so that
we have somewhere to place the visible widgets that we will drag and drop onto the scene.</p>
<ol><li>In the <tt>GraphSceneImpl</tt> class, declare the LayerWidget:
<pre class="examplecode">private LayerWidget mainLayer;</pre></li>
<li>In the <tt>GraphSceneImpl</tt> class's constructor, add the LayerWidget
as a child of the scene:
<pre class="examplecode">mainLayer = new LayerWidget (this);
addChild (mainLayer);</pre>
</li></ol>
<p>Now, when we drag and drop items from the palette as widgets to the scene,
we will add them as children of the LayerWidget. Because LayerWidgets are
transparent by default, you could have various LayerWidgets transparently on
top of each other so that, for example, you can add a background image to the scene.</p>
<p>For details, see <a href="http://bits.netbeans.org/dev/javadoc/org-netbeans-api-visual/org/netbeans/api/visual/widget/LayerWidget.html">LayerWidget</a>
in the Javadoc.</p>
<!-- ===================================================================================== -->
<h2><a name="adding-an-icon-widget"></a>Adding an IconNodeWidget via Drag and Drop Functionality</h2>
<p>Earlier, we used the <tt>GraphSceneImpl</tt> class's constructor to pass a scene to the TopComponent's JScrollPane.
So far, the scene exists but has no behavior. Behavior is added through actions.
The action that we will look at in this section is called <tt><a href="http://graph.netbeans.org/documentation.html#AcceptAction">AcceptAction</a></tt>. This
action enables drag and drop functionality. The drag and drop functionality could
be applied to a widget, but here we apply it to the scene itself.</p>
<p>We use <tt>createAcceptAction</tt> to specify what should happen
when an item from the palette is dragged over the scene. Two methods are involved
here. The first, <tt>isAcceptable()</tt>, is used to determine whether the
item is acceptable to the scene. Here you can test the item, by using
the transferable. You can also set the drag image, which is all that we
do in the implementation below. If <tt>true</tt> is returned, the <tt>accept</tt>
method is called. Here we get the image from the transferable, using the same
helper method as before. Then we call the <tt>addNode</tt> method, instantiating
a new <a href="http://bits.netbeans.org/dev/javadoc/org-netbeans-api-visual/org/netbeans/api/visual/widget/general/IconNodeWidget.html">IconNodeWidget</a>
and passing the image retrieved from the transferable.</p>
<p>All the code in this section is interrelated, and you will
receive red error underlines in your code until all the methods below
have been added, but we will try to add everything
in some kind of logical order anyway!</p>
<ol>
<li>First, add the <tt>createAcceptAction</tt>, with its two methods, to the <tt>GraphSceneImpl</tt> class's constructor:
<pre class="examplecode">getActions().addAction(ActionFactory.createAcceptAction(new AcceptProvider() {
@Override
public ConnectorState isAcceptable(Widget widget, Point point, Transferable transferable) {
Image dragImage = getImageFromTransferable(transferable);
JComponent view = getView();
Graphics2D g2 = (Graphics2D) view.getGraphics();
Rectangle visRect = view.getVisibleRect();
view.paintImmediately(visRect.x, visRect.y, visRect.width, visRect.height);
g2.drawImage(dragImage,
AffineTransform.getTranslateInstance(point.getLocation().getX(),
point.getLocation().getY()),
null);
return ConnectorState.ACCEPT;
}
@Override
public void accept(Widget widget, Point point, Transferable transferable) {
Image image = getImageFromTransferable(transferable);
Widget w = GraphSceneImpl.this.addNode(new MyNode(image));
w.setPreferredLocation(widget.convertLocalToScene(point));
}
}));</pre>
<p><strong class="notes">Note: </strong> After you add the above code, some red underlines will remain, denoting that there are errors.
These errors are because the code above refers to a method and a class that you have not yet created.
You will create them in the next steps.</p></li>
<li>Next, in the <tt>GraphSceneImpl</tt> class, add a helper method for retrieving the image from the transferable:
<pre class="examplecode">private Image getImageFromTransferable(Transferable transferable) {
Object o = null;
try {
o = transferable.getTransferData(DataFlavor.imageFlavor);
} catch (IOException ex) {
} catch (UnsupportedFlavorException ex) {
}
return o instanceof Image ? (Image) o : ImageUtilities.loadImage("org/netbeans/shapesample/palette/shape1.png");
}</pre>
<p><strong class="notes">Note: </strong> You can define
any kind of image when an image
is not successfully returned from this helper method. For now we will use the "<tt>shape1.png</tt>" image instead.</p>
</li>
<li>Create a new class called <tt>MyNode</tt>. This class represents a node in a graph-oriented model.
It cannot be an image directly, since each node has to be unique
(checked by "equals" method) in the model. If you would use the images
directly, then you would be able to have only 3 nodes (one for each
image) in the scene. Using the MyNode class, you can have multiple nodes and
each node can have its own or a shared image instance.
<pre class="examplecode">public class MyNode {
private Image image;
public MyNode(Image image) {
this.image = image;
}
public Image getImage() {
return image;
}
}</pre>
</li>
<li>Change the signature of the <tt>GraphSceneImpl</tt> class to the following, so that the node is
received by the Visual Library implementation class:
<pre class="examplecode">extends GraphScene&lt;MyNode, String&gt;</pre>
<p class="tips">You must let the IDE generate new stubs for the abstract methods.</p>
</li>
<li>Finally, define the new widget in the <tt>GraphSceneImpl</tt> class. This method is called automatically by the <tt>accept</tt>
method. Use it to define a Visual Library widget when the palette item is dropped.
<pre class="examplecode">@Override
protected Widget attachNodeWidget(MyNode node) {
IconNodeWidget widget = new IconNodeWidget(this);
widget.setImage(node.getImage());
widget.setLabel(Long.toString(node.hashCode()));
widget.getActions().addAction(ActionFactory.createMoveAction());
mainLayer.addChild(widget);
return widget;
}</pre>
<p>Notice that we set the image retrieved from the node. We also generate a random
number so that we have a label. By default, the widget exists but has no behavior.
Here, we create a move action, so that the widget can be moved in the scene. Finally,
before returning the widget to the scene, we add it as a child to the LayerWidget that
we created in the previous section.</p></li>
<li><p>Run the module and open the Shape window again.</p>
<p>Now you can drag and drop items from the palette. As you drag an item over the scene,
you will see the drag image. When you drop an item, it becomes a widget and is visible
within the scene as well as within the satellite view, as you can see here:</p>
<p><img alt="" src="../../images/tutorials/vislib/finishedscene.png"/></p>
</li>
</ol>
<!-- ===================================================================================== -->
<h2><a name="adding-to-scene"></a>Adding Functionality to the Scene</h2>
<p>In the previous section, we added <tt><a href="http://graph.netbeans.org/documentation.html#AcceptAction">AcceptAction</a></tt>
to the scene. We had to define two methods to specify whether the item should
be dropped and for resolving the item. In this section, we use <tt><a href="http://graph.netbeans.org/documentation.html#ZoomAction">ZoomAction</a></tt>,
to add zoom/unzoom functionality to the scene.</p>
<ol>
<li>At the end of the <tt>GraphSceneImpl</tt> class's constructor, add this line:
<pre class="examplecode">getActions().addAction(ActionFactory.createZoomAction());</pre></li>
<li>Install the module again.</li>
<li><p>While holding CTRL key, use the mouse wheel to zoom in and out of the scene:</p>
<p><img alt="" src="../../images/tutorials/vislib/zoom.png"/></p>
<p><img alt="" src="../../images/tutorials/vislib/unzoom.png"/></p>
</li>
</ol>
<p><strong class="notes">Note: </strong> The shapes are rendered as images. SVG is currently not supported.</p>
<p>In the same way as described above, you can add Pan functionality to the scene,
by means of this line:</p>
<pre class="examplecode">getActions().addAction(ActionFactory.createPanAction());</pre>
<p>When you add this line, the user will be able to hold down the mouse wheel
and then scroll in any direction in the scene.</p>
<!-- ===================================================================================== -->
<h2><a name="adding-to-widget"></a>Adding Functionality to the IconNodeWidget</h2>
<p>Earlier we added <tt><a href="http://graph.netbeans.org/documentation.html#MoveAction">MoveAction</a></tt>
to the IconNodeWidget, to enable move behavior for the widget. In the same way, a lot
of other behavior can be added to the widget. In this section, we add
<tt><a href="http://graph.netbeans.org/documentation.html#HoverAction">HoverAction</a></tt>,
<tt><a href="http://graph.netbeans.org/documentation.html#SelectAction">SelectAction</a></tt>,
and <tt><a href="http://graph.netbeans.org/documentation.html#InplaceEditorAction">InplaceEditorAction</a></tt>.</p>
<p>The <tt>InplaceEditorAction</tt> will let the user change the label:</p>
<p><img alt="" src="../../images/tutorials/vislib/editable.png"/></p>
<p>The <tt>SelectAction</tt> will change the color of the label when the widget is selected,
while the <tt>HoverAction</tt> will change the color of the label when the mouse hovers over the widget:</p>
<p><img alt="" src="../../images/tutorials/vislib/selectable-hoverable.png"/></p>
<ol>
<li>First define the editor action that we will add to the IconNodeWidget:
<pre class="examplecode">private WidgetAction editorAction = ActionFactory.createInplaceEditorAction(new LabelTextFieldEditor());</pre>
</li>
<li>Now define the <tt>LabelTextFieldEditor</tt>, as follows:
<pre class="examplecode">private class LabelTextFieldEditor implements TextFieldInplaceEditor {
public boolean isEnabled(Widget widget) {
return true;
}
public String getText(Widget widget) {
return ((LabelWidget) widget).getLabel();
}
public void setText(Widget widget, String text) {
((LabelWidget) widget).setLabel(text);
}
}</pre>
</li>
<li>Finally, assign the editor action to the IconNodeWidget, in the same way
as done for the move action earlier:
<pre class="examplecode">widget.getLabelWidget().getActions().addAction(editorAction);</pre>
<p>Here, we first get the IconNodeWidget's LabelWidget. Then we add the editor action
to the LabelWidget.</p>
</li>
<li>The IDE will prompt you to add several import statements. In each case,
accept the suggestion offered by the IDE.</li>
<li>Next, in the case of <tt>SelectAction</tt> and <tt>HoverAction</tt>, you need do
nothing more than assign these actions to the IconNodeWidget:
<pre class="examplecode">widget.getActions().addAction(createSelectAction());
widget.getActions().addAction(createObjectHoverAction());</pre>
</li>
<li>Next, you need to think about the order of the actions that
you have created. For details, see the <a href="http://bits.netbeans.org/dev/javadoc/org-netbeans-api-visual/org/netbeans/api/visual/widget/doc-files/documentation.html#OrderOfActions">Order of Actions</a>
section in the documentation. After you have reordered
the actions, the <tt>attachNodeWidget</tt> should look as follows:
<pre class="examplecode">protected Widget attachNodeWidget(MyNode node) {
IconNodeWidget widget = new IconNodeWidget(this);
widget.setImage(node.getImage());
widget.setLabel(Long.toString(node.hashCode()));
//double-click, the event is consumed while double-clicking only:
widget.getLabelWidget().getActions().addAction(editorAction);
//single-click, the event is not consumed:
widget.getActions().addAction(createSelectAction());
//mouse-dragged, the event is consumed while mouse is dragged:
widget.getActions().addAction(ActionFactory.createMoveAction());
//mouse-over, the event is consumed while the mouse is over the widget:
widget.getActions().addAction(createObjectHoverAction());
mainLayer.addChild(widget);
return widget;
}</pre>
</li>
<li>Install and try out the module again. As shown at the start of this section,
when you hover over a widget's label, or when you select it, its color will change. Also,
when you click on a label, you are able to edit its content.
</li>
</ol>
<p>Congratulations, you have completed your first Visual Library scene.</p>
<div class="feedback-box"><a href="https://netbeans.org/about/contact_form.html?to=3&amp;subject=Feedback:%20Visual%20Library%20API%207.1%20Tutorial%20NetBeans">Send Us Your Feedback</a></div>
<h2><a name="next_steps"></a>Next Steps</h2>
<p>For more information
on working with the Visual Library API, see:</p>
<ul>
<li><a href="http://graph.netbeans.org/">Visual Library Project Page</a></li>
<li><a href="http://graph.netbeans.org/documentation.html">Visual Library 2.0 - Documentation</a></li>
</ul>
</body>
</html>