blob: cfe764cfb5622c4b6a9b75453ee8e5471f1b008d [file] [log] [blame]
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<link rel="stylesheet" type="text/css" href="https://netbeans.org/netbeans.css">
<title>
NetBeans Platform Paint Application Tutorial for NetBeans Platform 6.5
</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" >
<link rel="shortcut icon" href="https://netbeans.org/favicon.ico" type="image/x-icon" >
<meta name="keywords" content="NetBeans, IDE, Platform, free, open source, developer" >
<h1>NetBeans Platform Paint Application Tutorial</h1>
<p>This tutorial takes you through the basics of using NetBeans IDE to develop rich-client applications
on top of the NetBeans Platform. When you develop applications on top of the NetBeans Platform, you are
developing on top of the NetBeans IDE's core. All the modules belonging to the IDE that are not relevant to
your application are excluded, but those that are useful are kept. By reusing features readily available in IDE's core,
you save yourself a lot of time and energy.
<p><strong class="notes">Note: </strong>This document uses the NetBeans IDE 6.5 Release. If you
are using NetBeans IDE 6.x, see <a href="60/nbm-paintapp.html">the 6.0/6.1 version
of this document</a>.
<p><b>Contents</b></p>
<img src="../images/articles/65/netbeans-stamp65.gif" class="stamp" width="114" height="114" alt="Content on this page applies to NetBeans IDE 6.5" title="Content on this page applies to NetBeans IDE 6.5"> </p>
<ul class="toc">
<li><a href="#intro">Introduction to the Paint Application</a></li>
<li><a href="#setup">Setting Up the Paint Application</a></li>
<ul>
<li><a href="#creatingModuleSuite">Creating the Application Skeleton</a></li>
<li><a href="#creatingLibWrapModule">Creating the Library Wrapper Module</a></li>
<li><a href="#creatingModProj">Creating the Module</a></li>
<li><a href="#specifyingModProjDep">Specifying the Module's Dependencies</a></li>
</ul>
<li><a href="#impMod">Creating and Embedding the Paint Canvas</a></li>
<ul>
<li><a href="#creatingCanv">Creating the Canvas</a></li>
<li><a href="#prepTopComp">Preparing the TopComponent Class</a></li>
<li><a href="#initTopComp">Initializing the TopComponent Class</a></li>
<li><a href="#fillSkelMeth">Filling out the Skeleton Methods</a></li>
<li><a href="#savingImage">Saving the Image to Disk</a></li>
</ul>
<li><a href="#defNew">Creating the New Canvas Menu Item</a></li>
<li><a href="#defSave">Creating the Save Canvas Menu Item</a></li>
<li><a href="#wrappingUp">Wrapping Up</a></li>
<li><a href="#creatingDist">Creating a Distribution</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 6.5</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<br/>version 5</td>
</tr>
</tbody>
</table>
<p><a name="intro"></a><h2>Introduction to the Paint Application</h2>
<p>This tutorial is designed to get you going as quickly as possible.
You will create and install a simple application on the NetBeans Platform. The application allows the user to
paint on the screen and save the results:<br>
<p><a name="sampleImage"></a><img border="1" src="../images/tutorials/paintapp/result-without-menus-60.png" alt="image of completed application">
<p>This initial version is far from a full fledged
paint application, but it demonstrates a very simple case of creating an application on top of the NetBeans Platform.
<p><b class="notes">Note:</b> The
<a href="nbm-google.html">NetBeans Plugin Quick Start</a> is a better tutorial for you if, instead of
rich-client applications, you want to
learn about NetBeans modules.
<p>In this tutorial, we recreate an application that
is a sample delivered with the IDE. To see the final
product, or to troubleshoot problems while working
through this tutorial, get the sample from the
New Project wizard, in the location shown below:</p>
<p align="left"><img border="1" src="../images/tutorials/paintapp/sample-in-new-project-60.png" alt="name and location panel">
<p><a name="setup"></a><h2>Setting Up the Paint Application</h2>
<p>In this section, you create the structure of your application. You first need to create an application skeleton, which you
can do via a wizard. The application depends on a library, so you will also create a library wrapper module that will
contain the library's JAR file. Finally, you will create the module that will contain your code.
<div class="indent">
<p><a name="creatingModuleSuite"></a><h3 class="tutorial">Creating the Application Skeleton</h3>
<p>The "NetBeans Platform Application" template will create your application's skeleton.
The skeleton will consist of a set of modules that
work together to form the basis of your application. You will use the Project Properties dialog to assign your application's splashscreen,
application name, and the type and number of NetBeans modules that you want to use. You can also take advantage
of such actions as creating a ZIP distribution and building a Java WebStart (JNLP) application, which are
important tools in making your application available to other users.
<ol>
<li>Choose File &gt; New Project. Under Categories, select NetBeans Modules. Under projects,
select NetBeans Platform Application:
<p><p align="left"><img src="../images/tutorials/paintapp/paintapp-proj-wiz.png" alt="project template">
<p>Click Next.</li>
<li>In the Name and Location panel, type <tt>PaintApp</tt> in Project Name.
Change the
Project Location to any directory on your computer. Leave the Set as Main Project checkbox selected. Click Finish.
</ol>
<p>The new application skeleton opens in the IDE. It contains two nodes in the Projects window. The first node, the "Modules" node,
is for manually adding modules and library wrapper modules to the application. In addition, when you use the Module wizard or the
Library Wrapper Module wizard, the module that you create can automatically be added to the application.
<p><h3 class="tutorial"><a name="creatingLibWrapModule"></a>Creating the Library Wrapper Module</h3>
<p>A library wrapper module is a module whose JAR file contains no code&#8212;it is just a
pointer to a library. It turns the library into a NetBeans module, so that all the protections of the NetBeans
classloader system apply to it&#8212;without modifying the original JAR file. Your application can then depend on the
library just as if the library were just another NetBeans module. And, if new versions of the library become available,
you can distribute them without needing to distribute anything except a single NetBeans Module (NBM) file for the wrapper library.
<p><b class="notes">Note:</b> One of the benefits of building on the NetBeans Platform is that its user interface is based on Swing&#8212;the
standard user interface toolkit for Java. Since Swing has been around for a long time, there are a lot
of Swing components you can reuse in your application. In this tutorial, you reuse an existing color chooser
JavaBean (you can find the source for it in NetBeans CVS under <tt>contrib/coloreditor</tt>). The JAR file is called <tt>ColorChooser.jar</tt>.
You can download the library <a href="https://colorchooser.dev.java.net/">here</a>.
Save it anywhere in your filesystem.
<p>Do the following to create a library wrapper module for the
<tt>ColorChooser.jar</tt> file:
<ol>
<li>Choose File &gt; New Project. Under Categories, select NetBeans Modules. Under projects,
select Library Wrapper Module and click Next.</li>
<li>In the Select Library panel, for the Library text box, either type in the path to <tt>ColorChooser.jar</tt> or browse to its location.
<li>Leave the License text field empty. If you intend to distribute the completed product, you should
include the external library's license file. Click Next.
<li>In the Name and Location panel, fill in the project name, set the project location, and make
sure that the Add to Module Suite drop-down shows that the module will be added to the application. Click Next.
<li>In the Basic Module Configuration panel, type a unique name in the code name base, specify a module display
name, and the location of the module's localizing bundle:</p>
<p align="left"><img src="../images/tutorials/paintapp/lib-wrap-1.png" alt="name and location panel">
<p>Click Finish.</ol>
<p>The module that wraps the selected <tt>colorchooser.jar</tt> is created by the IDE.
The structure of the new module is shown in the Projects window. The "Modules" node in the application's structure
shows that the module is part of the application.
<h3 class="tutorial"><a name="creatingModProj"></a>Creating the Module</h3>
<p>Now you need a module to contain the actual code you're going to write.
<ol>
<li>Choose File &gt; New Project. Under Categories, select NetBeans Modules. Under projects,
select Module and click Next.</li>
<li>In the Name and Location panel, type <tt>Paint</tt> in Project Name.
Change the
Project Location to any directory on your computer. Ensure that the Add to Module Suite radio button is selected
and that the <tt>PaintApp</tt> application is selected in the Module Suite drop down list. Select the Set as Main Project checkbox.
Click Next.
<li>In the Basic Module Configuration panel, type <tt>org.netbeans.paint</tt>. Leave <tt>Paint</tt> as the Module Display Name.
Leave the location of the localizing bundle. Click Generate XML Layer and do not change the
suggested location, so that the localizing bundle and the XML layer file will be stored in a
package with the name <tt>org.netbeans.paint</tt>. </p>
<p>These files do the following:
<ul>
<li><b>Localizing Bundle.</b> Specifies language-specific strings for internationalization.</li>
<li><b>XML Layer.</b> Registers items such as menus and toolbar buttons in the NetBeans Platform application.
</li>
</ul>
<p>Click Finish.</ol>
<p> The IDE creates the <tt>Paint</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). For example, the Projects window should look as follows:</p>
<p align="left"><img src="../images/tutorials/paintapp/paintapp-start-1.png" alt="paint app">
<p>In addition to the localizing bundle and the XML layer, the project also includes the following important files:
<ul>
<li><b>Module Manifest.</b> Declares that the project is a module. In addition, it sets some module-specific settings, such
as the location of the XML layer, the location of the localizing bundle, and the module version.
<li><b>Build Script.</b> Provides a place where you can create your own Ant targets and override those that are
specified in <tt>nbproject/build-impl.xml</tt>.
<li><b>Project Metadata.</b> Contains information such as the project's type, contents, platform, classpath,
dependencies, and mappings between project commands and targets in Ant scripts.
</ul>
<p>You will not need to modify any of these files during this tutorial.
</li>
<p><h3 class="tutorial"><a name="specifyingModProjDep"></a>Specifying the Module's Dependencies</h3>
<p>You will need to subclass several classes that belong to the <a href="http://bits.netbeans.org/dev/javadoc/index.html">NetBeans APIs</a>.
In addition, the project depends on the <tt>ColorChooser.jar</tt> file.
All NetBeans APIs are implemented by modules, so completing both of these tasks really just means
adding some modules to the list of modules that our module needs in order to run.
<ol>
<li>In the Projects window, right-click the <tt>Paint</tt> project node and choose Properties.
The Project Properties dialog box opens. Under Categories, click Libraries.</li>
<li>For each of the API's listed in the table below, click "Add Dependency..." and then, in the Filter text box, start typing the name of the
class that you want to subclass.<br><br>
<p><table width="76%" border="1">
<tbody>
<tr>
<td>
<div align="left"><b>Class</b></div>
</td>
<td>
<div align="left"><b>API</b></div>
</td>
<td>
<div align="left"><b>Purpose</b></div>
</td>
</tr>
<tr>
<td><tt>ColorChooser</tt></td>
<td><tt>ColorChooser</tt></td>
<td>Library wrapper module for the color chooser component you created</td>
</tr>
<tr>
<td><tt>DataObject</tt></td>
<td><tt>Datasystems API</tt></td>
<td>The NetBeans module containing the DataObject class</td>
</tr>
<tr>
<td><tt>DialogDisplayer</tt></td>
<td><tt>Dialogs API</tt></td>
<td>This allows the creation of user notification, a dialog's description and
permits it to be displayed</td>
</tr>
<tr>
<td><tt>AbstractFile</tt></td>
<td><tt>File System API</tt></td>
<td>This provides a common API to access files in a uniform manner</td>
</tr>
<tr>
<td><tt>AbstractNode</tt></td>
<td><tt>Nodes API</tt></td>
<td>This serves as the main aparatus for visualisation of objects in NetBeans</td>
</tr>
<tr>
<td><tt>StatusDisplayer</tt></td>
<td><tt>UI Utilities API</tt></td>
<td>The StatusDisplayer class used to write the statusbar in the main window</td>
</tr>
<tr>
<td><tt>WeakListeners</tt></td>
<td><tt>Utilities API</tt></td>
<td>This contains the WeakListeners class</td>
</tr>
<tr>
<td><tt>TopComponent</tt></td>
<td><tt>Window System API</tt></td>
<td>This contains the TopComponent JPanel class</td>
</tr>
</tbody>
</table>
<p><p>The first column in the table above lists all the classes that you will subclass in this tutorial.
In each case, start typing the class name in the Filter and watch the Module list narrow. Use the table's second column to pick the appropriate API (or, in the case of <tt>ColorChooser</tt>, the library) from the narrowed Module list and then click OK to confirm the choice:
<p align="left"><img border="1" src="../images/tutorials/paintapp/libfilter-60.png" alt="initial-proj-window">
<li>Click OK to exit the Project Properties dialog box.
<li>In the Projects window, expand the Paint module's project node if it is not already expanded. Then expand the Important Files node and double-click the Project Metadata node. Note that the API's you selected have been declared as module dependencies.
</ol>
</div><br>
<h2><a name="impMod"></a>Creating and Embedding the Paint Canvas</h2>
<div class="indent">
<h3 class="tutorial"><a name="creatingCanv"></a>Creating the Canvas</h3>
<p>The next step is to create the actual component on which the user can paint. Here, you use a pure Swing component&#8212;so, let's
skip the details of its implementation and just provide the final version. The color chooser bean, which you created the library
wrapper module for, is used in the source code for this panel&#8212;when you run the finished application, you will see it
in the toolbar of the panel for editing images.</p>
<ol type="1">
<li>In the Projects window, expand the <tt>Paint</tt> node, then expand the Source Packages node, and then
right-click the <tt>org.netbeans.paint</tt> node. Choose New &gt; Java Class.</li>
<li>Enter <tt>PaintCanvas</tt> as the Class Name. Ensure that <tt>org.netbeans.paint</tt> is listed as the
Package. Click Finish. <tt>PaintCanvas.java</tt> opens in the Source editor.
<li>Replace the default content of the file with the content found
<a target="source" href="https://platform.netbeans.org/guide/tutorials/paintTutorial/PaintCanvas.java">here</a>.
If you named your package something other than <tt>org.netbeans.paint</tt>, correct the package name in the
Source editor.</li>
</ol>
<p><h3 class="tutorial"><a name="prepTopComp"></a>Preparing the TopComponent Class</h3>
<p><p>Now you'll write your first class that touches the <a href="http://bits.netbeans.org/dev/javadoc/index.html">NetBeans APIs</a>. It is a <tt><a href="http://bits.netbeans.org/dev/javadoc/org-openide-windows/org/openide/windows/TopComponent.html">TopComponent</a></tt> class. A <tt>TopComponent</tt> class is
just a <tt>JPanel</tt> class which NetBeans' windowing system knows how to talk to&#8212;so it can be put inside a tabbed
container inside the main window.
<ol type="1">
<li>In the Projects window, expand the <tt>Paint</tt> node, then expand the Source Packages node, and then
right-click the <tt>org.netbeans.paint</tt> node. Choose New &gt; Java Class.</li>
Enter <tt>PaintTopComponent</tt> as the Class Name. Ensure that <tt>org.netbeans.paint</tt> is listed as the
Package. Click Finish. <tt>PaintTopComponent.java</tt> opens in the Source editor.
<li>Near the top of the file, change the class declaration to the following:
<pre class="examplecode">public class PaintTopComponent extends TopComponent implements ActionListener, ChangeListener {</pre></li>
<li>Press Ctrl-Shift-I to fix imports anc click OK in the dialog box. The IDE makes the necessary import package declarations at the top of the file.</p>
<p>Notice the red line under the class declaration that you just entered. Position the cursor in the line and notice that a
light bulb appears in the left margin. Click the light bulb (or press Alt-Enter), as shown below:
<p><img border="1" src="../images/tutorials/paintapp/lightbulb-60.png" alt="Lightbulb.">
<p>Select Implement all
abstract methods. The IDE generates two method skeletons&#8212;<tt>actionPerformed()</tt> and <tt>stateChanged()</tt>. You fill these
out later in this tutorial.</li>
<li>Add the following three variable declarations to the top of the <tt>PaintTopComponent</tt> class and
then fix the import statements (Ctrl-Shift-I).
<pre class="examplecode"> private PaintCanvas canvas = new PaintCanvas(); //The component the user draws on
private JComponent preview; //A component in the toolbar that shows the paintbrush size
private static int ct = 0; //A counter you use to provide names for new images</pre></li>
<li>Now you need to implement two boilerplate methods. The first one tells the windowing system to disregard
open windows when the application is shut down; the second provides a base string for a unique string ID for our component.
Each <tt>TopComponent</tt> has a unique string ID that is used when saving the <tt>TopComponent</tt>.
Insert the following two methods into the <tt>PaintTopComponent</tt> class:
<pre class="examplecode"> @Override
public int getPersistenceType() {
return PERSISTENCE_NEVER;
}
@Override
public String preferredID() {
return "Image";
}</pre></li>
</ol>
<p>The class should now look as follows:
<pre class="examplecode">public class PaintTopComponent extends TopComponent implements ActionListener, ChangeListener {
private PaintCanvas canvas = new PaintCanvas(); //The component the user draws on
private JComponent preview; //A component in the toolbar that shows the paintbrush size
private static int ct = 0; //A counter you use to provide names for new images
public PaintTopComponent() {
}
@Override
public void actionPerformed(ActionEvent arg0) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void stateChanged(ChangeEvent arg0) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public int getPersistenceType() {
return PERSISTENCE_NEVER;
}
@Override
public String preferredID() {
return "Image";
}
}</pre>
<h3 class="tutorial"><a name="initTopComp"></a>Initializing the TopComponent Class</h3>
<p>In this section, we add code
that initializes the user interface.
<ol type="1">
<li>Define the constructor and then fix the import statements (Ctrl-Shift-I):
<pre class="examplecode"> public PaintTopComponent() {
initComponents();
String displayName = NbBundle.getMessage(
PaintTopComponent.class,
"UnsavedImageNameFormat",
new Object[] { new Integer(ct++) }
);
setDisplayName(displayName);
}</pre></li>
<p>The code here is pretty simple. The first call is to a method you haven't written yet, <tt>initComponents()</tt>,
which will add a toolbar and a PaintCanvas to your <tt>TopComponent</tt>. Because you haven't written the method yet,
a red line appears underneath it here. As before, click the light bulb (or press Alt-Enter) and accept the suggestion:
<p><img border="1" src="../images/tutorials/paintapp/lightbulb-initcomponents-60.png" alt="Lightbulb.">
<p>The <tt>initComponents()</tt> method skeleton is generated for you.
<li>Expand the <tt>org.netbeans.paint</tt> package in the Projects window. Double-click the <tt>Bundle.properties</tt> file
to open it in the Source editor. Add the following line to the end:
<pre class="examplecode"> UnsavedImageNameFormat=Image {0}</pre>
<p>This specifies the text that will be used to identify a new image file in the application prior to being saved by the user.
For example, when a user clicks New Canvas for the first time in your completed application, a tab will appear above
the Source Editor with the label, 'Image 0'. Make sure that you save the file before continuing.
</li></ol>
<h3 class="tutorial"><a name="fillSkelMeth"></a>Filling Out the Skeleton Methods</h3>
<p>In this section, we code the user interface of
our application. We could also use the IDE's GUI
Builder to visually design the layout.
<ol>
<li>The <tt>initComponents()</tt> method installs components in your panel, so
that the user has something to interact with. You generated its skeleton method during
the previous section in the <tt>PaintTopComponent.java</tt> class. Fill it out as follows:
<pre class="examplecode"> private void initComponents() {
setLayout(new BorderLayout());
JToolBar bar = new JToolBar();
ColorChooser fg = new ColorChooser();
preview = canvas.createBrushSizeView();
//Now build our toolbar:
//Make sure components don't get squished:
Dimension min = new Dimension(32, 32);
preview.setMaximumSize(min);
fg.setPreferredSize(new Dimension(16, 16));
fg.setMinimumSize(min);
fg.setMaximumSize(min);
JButton clear = new JButton(
NbBundle.getMessage(PaintTopComponent.class, "LBL_Clear"));
JLabel fore = new JLabel(
NbBundle.getMessage(PaintTopComponent.class, "LBL_Foreground"));
fg.addActionListener(this);
clear.addActionListener(this);
JSlider js = new JSlider();
js.setMinimum(1);
js.setMaximum(24);
js.setValue(canvas.getDiam());
js.addChangeListener(this);
fg.setColor(canvas.getColor());
bar.add(clear);
bar.add(fore);
bar.add(fg);
JLabel bsize = new JLabel(
NbBundle.getMessage(PaintTopComponent.class, "LBL_BrushSize"));
bar.add(bsize);
bar.add(js);
bar.add(preview);
JLabel spacer = new JLabel(" "); //Just a spacer so the brush preview
//isn't stretched to the end of the
//toolbar
spacer.setPreferredSize(new Dimension(400, 24));
bar.add(spacer);
//And install the toolbar and the painting component:
add(bar, BorderLayout.NORTH);
add(canvas, BorderLayout.CENTER);
}</pre></li>
<p>Press Ctrl-Shift-I to generate the required import statements.
<li>Fill out the other two methods that you generated. They are used for listening to the
<tt>PaintTopComponent</tt> class:
<pre class="examplecode"> public void actionPerformed(ActionEvent e) {
if (e.getSource() instanceof JButton) {
canvas.clear();
} else if (e.getSource() instanceof ColorChooser) {
ColorChooser cc = (ColorChooser) e.getSource();
canvas.setPaint (cc.getColor());
}
preview.paintImmediately(0, 0, preview.getWidth(), preview.getHeight());
}</pre>
<pre class="examplecode"> public void stateChanged(ChangeEvent e) {
JSlider js = (JSlider) e.getSource();
canvas.setDiam (js.getValue());
preview.paintImmediately(0, 0, preview.getWidth(), preview.getHeight());
}</pre>
</li>
<li>In the <tt>Bundle.properties</tt> file, add the following key-value pairs to the end of the file:
<pre class="examplecode">
LBL_Clear = Clear
LBL_Foreground = Foreground
LBL_BrushSize = Brush Size
</pre>
<p>Make sure that you save the file before continuing.
</li>
</ol>
<h3 class="tutorial"><a name="savingImage"></a>Saving the Image to Disk</h3>
<p>In your new application, it would be a good idea to allow users to save the images they create.
By including the following code into the <tt>PaintTopComponent</tt> class, this functionality will be activated.</p>
<ol type="1">
<li>Insert the following code into the <tt>PaintTopComponent</tt> class:
<pre class="examplecode"> public void save() throws IOException {
if (getDisplayName().endsWith(".png")) {
doSave(new File(getDisplayName()));
} else {
saveAs();
}
}</pre>
<pre class="examplecode"> public void saveAs() throws IOException {
JFileChooser ch = new JFileChooser();
if (ch.showSaveDialog(this) == JFileChooser.APPROVE_OPTION && ch.getSelectedFile() != null) {
File f = ch.getSelectedFile();
if (!f.getPath().endsWith(".png")) {
f = new File(f.getPath() + ".png");
}
if (!f.exists()) {
if (!f.createNewFile()) {
String failMsg = NbBundle.getMessage(
PaintTopComponent.class,
"MSG_SaveFailed", new Object[] { f.getPath() }
);
JOptionPane.showMessageDialog(this, failMsg);
return;
}
} else {
String overwriteMsg = NbBundle.getMessage(
PaintTopComponent.class,
"MSG_Overwrite", new Object[] { f.getPath() }
);
if (JOptionPane.showConfirmDialog(this, overwriteMsg)
!= JOptionPane.OK_OPTION) {
return;
}
}
doSave(f);
}
}</pre>
<pre class="examplecode"> private void doSave(File f) throws IOException {
BufferedImage img = canvas.getImage();
ImageIO.write(img, "png", f);
String statusMsg = NbBundle.getMessage(PaintTopComponent.class,
"MSG_Saved", new Object[] { f.getPath() });
StatusDisplayer.getDefault().setStatusText(statusMsg);
setDisplayName(f.getName());
}</pre></li>
<li>Add the following lines to the <tt>Bundle.properties</tt> file:
<pre class="examplecode"> MSG_SaveFailed = Could not write to file {0}
MSG_Overwrite = {0} exists. Overwrite?
MSG_Saved = Saved image to {0}</pre>
<p>Make sure that you save the file before continuing.
</li>
<li>Click Ctrl-Shift-I to fix the import statements. You will notice that there are
two fully qualified names for the <tt>File</tt> class. Choose the <tt>java.io.File</tt> option.
</ol>
</div><br>
<h2><a name="defNew"></a>Creating the New Canvas Menu Item</h2>
<p>You use the Module Development file templates to create the basis of the module's
functionality. When you use a file template, the IDE registers the item that you create
in the <tt>layer.xml</tt> file. After using a wizard to create the file template, you use
the <a href="https://netbeans.org/download/dev/javadoc/">NetBeans APIs</a> to continue developing the module.
<ol>
<li>In the Projects window, right-click the Paint module's project node and
choose New &gt; Other. In the New File wizard, choose Module Development
under Categories and Action under File Types. Click Next.</li>
<li>In the Action Type panel, accept the defaults. Click Next.
<li>In the GUI Registration panel, select Global Menu Item, and select
Global Toolbar Button. Set the following values:
<p>
<ul><li><b>Category:</b> Edit
<li><b>Menu:</b> File
<li><b>Position:</b> Anywhere you want it to be!
<li><b>Toolbar:</b> File
<li><b>Position:</b> Anywhere you want it to be!
</ul>
<p><b class="notes">Note:</b> Where you position the action is not important, as long as it is in the File menu and in the File toolbar.
<p>You should now see the following:
<p><p align="left"><img src="../images/tutorials/paintapp/newcanvasaction-60.png" alt="GUI Registration panel.">
<p>Click Next.
<li>In the Name, Icon, and Location panel, type <tt>NewCanvasAction</tt> in Class Name
and type <tt>New Canvas</tt> in Display Name.
<p>In Icon, browse to this icon (right-click it here and then save it in the <tt>org.netbeans.paint</tt> folder):
<img src="../images/tutorials/paintapp/new_icon.png" alt="New Canvas icon.">
<li>Click Finish.</p>
<p>The IDE creates <tt>NewCanvasAction.java</tt> in <tt>org.netbeans.paint</tt> and opens
it in the Source Editor. This is what you should see:
<pre class="examplecode">/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.netbeans.paint;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public final class NewCanvasAction implements ActionListener {
public void actionPerformed(ActionEvent e) {
// TODO implement action body
}
}</pre>
<p>As specified in
the GUI Registration panel, the IDE
registers the action class as a menu item and as a toolbar button in the <tt>layer.xml</tt> file, together
with information about the icon and display name.
<li>In the Source Editor, open <tt>NewCanvasAction.java</tt> and fill out the <tt>actionPerformed()</tt> method as follows:
<pre class="examplecode"> public void actionPerformed(ActionEvent e) {
PaintTopComponent tc = new PaintTopComponent();
tc.open();
tc.requestActive();
}</pre>
<p>What this does is simply to create a new instance of our image editing
component, open it, so it appears in the main window, and activate it by sending keyboard focus to it and selecting its tab.
</ol>
<h2 class="tutorial"><a name="defSave"></a>Creating the Save Canvas Menu Item</h2>
<p>As in the previous section, we use the New Action wizard to create a menu item, this
time for saving images.
<ol>
<li>In the Projects window, right-click the Paint module's project node and
choose New &gt; Other. In the New File wizard, choose Module Development
under Categories and Action under File Types. Click Next.</li>
<li>In the Action Type panel, accept the defaults. Click Next.
<li>In the GUI Registration panel, select Global Menu Item, and select
Global Toolbar Button. Set the following values:
<p>
<ul><li><b>Category:</b> Edit
<li><b>Menu:</b> File
<li><b>Position:</b> Anywhere you want it to be!
<li><b>Toolbar:</b> File
<li><b>Position:</b> Anywhere you want it to be!
</ul>
<p><b class="notes">Note:</b> Where you position the action is not important, as long as it is in the File menu and in the File toolbar.
<p><p>Click Next.
<li>In the Name, Icon, and Location panel, type <tt>SaveCanvasAction</tt> in Class Name
and type <tt>Save Canvas</tt> in Display Name. </p>
<p>In Icon, paste this icon (right-click it here and then save it in the <tt>org.netbeans.paint</tt> folder):
<img src="../images/tutorials/paintapp/save_icon.png" alt="Save Canvas icon.">
<li>Click Finish.</p>
<p>The IDE creates <tt>SaveCanvasAction.java</tt> in <tt>org.netbeans.paint</tt> and opens
it in the Source Editor.
<li>Change the class signature so that <tt>CallableSystemAction</tt> is extended and <tt>PropertyChangeListener</tt> is
implemented:
<pre class="examplecode">public final class SaveCanvasAction extends CallableSystemAction implements PropertyChangeListener</pre>
<li>In the Source Editor, make sure <tt>SaveCanvasAction.java</tt> is opened and fill out the <tt>actionPerformed()</tt> method as follows:
<pre class="examplecode"> @Override
public void actionPerformed(ActionEvent e) {
TopComponent tc = TopComponent.getRegistry().getActivated();
if (tc instanceof PaintTopComponent) {
try {
((PaintTopComponent) tc).saveAs();
} catch (IOException ioe) {
ErrorManager.getDefault().notify(ioe);
}
} else {
//Theoretically the active component could have changed
//between the time the menu item or toolbar button was
//pressed and when the action was invoked. Not likely,
//but theoretically possible
Toolkit.getDefaultToolkit().beep();
}
}</pre>
<p>Press Ctrl-Shift-I to generate the required import statements:
<p><img border="1" src="../images/tutorials/paintapp/fiximports-60.png" alt="Fix imports.">
<p><li>Fill out the methods from the <tt>CallableSystemAction</tt> class as follows:
<pre class="examplecode"> @Override
public String getName() {
return "Save Canvas";
}
@Override
public HelpCtx getHelpCtx() {
return null;
}
</pre>
<p><li>Fill out the <tt>propertyChange()</tt> method from the <tt>PropertyChangeListener</tt> as follows:
<pre class="examplecode"> @Override
public void propertyChange(PropertyChangeEvent evt) {
if (TopComponent.Registry.PROP_ACTIVATED.equals(evt.getPropertyName())){
updateEnablement();
}
}</pre><p>
<p>When a red line appears, click Alt + Enter to allow the IDE to create an <tt>updateEnablement()</tt> method in the <tt>SaveCanvasAction</tt> class.</p>
<p>Next, define the <tt>updateEnablement()</tt> method:
<pre class="examplecode"> private void updateEnablement() {
setEnabled(TopComponent.getRegistry().getActivated()
instanceof PaintTopComponent);
}</pre><p>
<p>Finally, define the constructor:
<pre class="examplecode"> public SaveCanvasAction() {
TopComponent.getRegistry().addPropertyChangeListener (
WeakListeners.propertyChange(this,
TopComponent.getRegistry()));
updateEnablement();
}</pre>
<p>When a red line appears, click Alt + Enter to allow the IDE to import <tt>org.openide.util.WeakListeners</tt>.</p>
<p>The main code of interest is the adding of the property change listener. <tt>TopComponent.Registry</tt> is a registry
of all opened <tt>TopComponents</tt> in the system&#8212;all the opened tabs. What we want to do is listen on it
for changes, and enable and disable the action depending on what has focus.
<p><p><b class="notes">Note:</b> Rather than directly attaching a property change listener, you call <tt>WeakListeners.propertyChange()</tt>.
What this does is generate a property change listener that weakly references your action. While in practice your action
will live as long as the application is open, it's a good practice, and future-proofing, to use a weak listener
if you're attaching a listener and there is no code that ever detaches it. Otherwise, you've got a potential memory leak&#8212;your
action could never be garbage collected because the registry is holding a reference to it in its list of listeners.
</ol>
<p>This is what you should now see in the Projects window:
<p><img src="../images/tutorials/paintapp/final-paint-module.png" alt="final view of Projects window">
<h2><a name="wrappingUp"></a>Wrapping Up</h2>
<p>Of course, you want to create a nicely finetuned applicationso there are a couple of final
steps you can perform. First you create a splash screen for your application
and then you create a ZIP distribution and a JNLP application.
<ol>
<li>Run the <tt>PaintApp</tt> project. After the application starts, size the main screen fairly small
and draw a splash screen. Use the Save button to save the splash screen. </li>
<li>In the original project, right-click the <tt>PaintApp</tt> node, choose Properties, and then click Build
in the Project Properties dialog box.</li>
<li>Select Create Standalone Application. Now you
can specify a branding name (which will be the name
of the launcher that the IDE can generate for you)
and an application title (which will appear in the
application's title bar). By default, you should
see the following:</p>
<p><img border="1" src="../images/tutorials/paintapp/splashscreen1-60.png" alt="splash screen">
<li>Click Splash Screen. Browse to your Splash Screen. If you do not have one,
you can use <a href="https://platform.netbeans.org/images/tutorials/paintapp/splash.gif">this one</a>.
Click OK to attach it to your application:</li>
<p><img border="1" src="../images/tutorials/paintapp/splashscreen-60.png" alt="splash screen">
<li>Now, in the Paint module's <tt>layer.xml</tt> file, add the following tags within the Menu
folder. These tags remove the GoTo and View menus, which your Paint application does not need.</p>
<pre class="examplecode">&lt;file name="GoTo_hidden"/&gt;
&lt;file name="View_hidden"/&gt;</pre>
<p>Alternatively, instead of adding the above tags manually, you can delete the folders within the
<tt>&lt;this layer in context&gt;</tt> node of the <tt>layer.xml</tt> file. To do this, expand <tt>&lt;this layer in context&gt;</tt>,
then expand the Menu Bar node. Choose Delete from the right-click menu of both the GoTo and View nodes.
<li>Finally, run the application again and notice the splash screen. Once the application has started up, notice
that the title bar displays the title that you specified. Also, there are a lot less
menu items, toolbar buttons, and other features:<br><br>
<p><img border="1" src="../images/tutorials/paintapp/result-without-menus-60.png" alt="result without menus">
</ol>
<h2 class="tutorial"><a name="creatingDist"></a>Creating a Distribution</h2>
<p>
Now it's time to choose the distribution medium. Right click the <tt>PaintApp</tt> node and choose Create ZIP Distribution
to package the entire application, with all needed modules and files, as a zip file.
You can also choose Build JNLP Application to create a JavaWebStart™ version of the application you can put on a web server
and link to directly from a web page (you will need to set a correct URL&#8212;the generated descriptor uses file: protocol
so you can test your web-startable distribution locally).
<p>That's it! Your first complete application built on top of the NetBeans Platform is finished. Next stop: <a href="https://platform.netbeans.org/tutorials/nbm-feedreader.html">NetBeans Platform Feed Reader Tutorial</a>.
<br>
<div class="feedback-box"><a href="https://netbeans.org/about/contact_form.html?to=3&amp;subject=Feedback: NetBeans Platform Paint Application Tutorial">Send Us Your Feedback</a></div>
<br style="clear:both;" />
<hr>
</body>
</html>