NetBeans Palette API Tutorial
This tutorial shows you how to use the Palette API and make a palette available for your
TopComponent.
You will also learn how to create a simple TopComponent using the wizard, how to display a status-bar message
and a dialog. But all these other things are just so that you'll learn how to display a nice palette for
your TopComponent, with icons, localized text and drag-and-drop support.
Downloads
You are going to need for this tutorial
- A functional NetBeans 5.x (with Java 1.4 or more)
- Optionally, the source code of this tutorial (download)
Creating the module
- Choose File > New Project. Under Categories, select
NetBeans Plug-in Modules. Under projects, select Module Project and
click Next.
- In the Name and Location panel, type palette-demo
in Project Name. Set the the Project Location and Project Folder to
whatever folder you fancy. Select the Standalone Modulde radio button
and select the Set as Main Project checkbox. Click Next.
- In the Basic Module Configuration panel, set the Code Name Base to com.example.palette (or your preffered package name) and the Module Display Name to Palette Demo. Press Finish.
Creating a window
As seen in the Palette API javadoc, you need to have an active TopComponent in order to see the palette. For help regarding TopComponents, please check some other NetBeans tutorials.
- In the Projects window, select the Palette Demo (this is not necessary if
Palette Demo is your only project open).
- Choose File > New File... .Under Categories, select NetBeans Module Development,
then Window Component under File Types. Click Next.
- In the Basic Settings panel, select editor for Window Position. Click Next.
- In the Name, Icon and Location panel, type Drawing. Click Finish.
The DrawingTopComponent should open in the GUI Editor and you should have some new generated
XML and Java files.
To see what you have managed so far, right-click the Palette Demo project and select
Install/Reload in Target Platform. A build should start and a new NetBeans instance should
run. In the new instance, select Window > Open Drawing Window. You should see an empty
window with the name Drawing Window.
Adding a palette
In order to use the Palette API you'll have to declare a dependency for your module.
- Right-click the Palette Demo > Properties.
- Under the Libraries category click Add... . Select then Core - Component Palette and
Nodes API. Click Ok.
A Palette contains one or more categories with items. First you'll have to define a new item.
- Right-click the project, select New -> File/Folder... Choose XML under Categories then XML
Document under File Types. Click Browse... and select src/com/example/palette (Click Select Folder).
Click Next.
-
In the Name and Location panel, type simple-item for the File Name. Click Next.
-
In the Select Document Type choose DTD-Constrained Document. Click Next.
-
In the DTD Options, choose from the list for DTD Public ID -//NetBeans//Editor Palette Item 1.0//EN.
Select editor_palette_item for the Document Root.
Click Finish.
You should see this:
<?xml version="1.0" encoding="UTF-8"?>
<!--
Document : simple-item.xml
Created on : February 25, 2006, 12:57 AM
Author : emi
Description:
Purpose of the document follows.
-->
<!DOCTYPE editor_palette_item PUBLIC '-//NetBeans//Editor Palette Item 1.0//EN'
"http://www.netbeans.org/dtds/editor-palette-item-1_0.dtd">
<editor_palette_item>
</editor_palette_item>
Make sure to add the missing part above.
We have now a barebone Palette Item. Open now the layer.xml file and add the following lines at the bottom,
before </ filesystem >
<folder name="MyPalette">
<folder name="Category1">
<file name="PaletteItem_1.xml" url="simple-item.xml"/>
</folder>
<folder name="Category2">
<file name="PaletteItem_2.xml" url="simple-item.xml"/>
</folder>
</folder>
What did we do? We declared a folder called MyPalette (which will represent our palette) and added some
inner folders. These will be the categories. The files inside the categories folders are the items.
The items may have any names you want but note the url refers to the simple-item.xml.
More items
In order to have more than one item, you'll need to create more XML files (like simple-item.xml) and refer
them via url in the layer.xml file. The are ways of programatically adding new items, but you'll have
to refer to the Palette API Javadoc for that.
Link it to the TopComponent
But so far there is no link between our rudimentary palette and our TopComponent. The Palette API javadoc
helps us here. We need to create a PaletteController then link it to the TopComponent (via Lookup).
- PaletteFactory.createPalette takes care of the creation part. It just needs the palette name (meaning the
folder declared in the layer.xml and the actions for the palette). Since we don't need any actions right now
we'll just create a dummy anonymous inner class.
private PaletteController initializePalette() {
try {
return PaletteFactory.createPalette( "MyPalette", new PaletteActions() {
public Action[] getCustomCategoryActions(Lookup lookup) {
return new Action[0];
}
public Action[] getCustomItemActions(Lookup lookup) {
return new Action[0];
}
public Action[] getCustomPaletteActions() {
return new Action[0];
}
public Action[] getImportActions() {
return new Action[0];
}
public Action getPreferredAction(Lookup lookup) {
return null; //TODO
}
});
} catch (IOException ex) {
ex.printStackTrace();
}
return null;
}
We add the initializePalette() method to the DrawingTopComponent class. Since there are going to be lots of import
problems an Alt + Shift + F should help (Source > Fix Imports). When you are asked about the Action class,
just select javax.swing.Action
-
We add the PaletteController in the Lookup of the TopComponent. We modify the DrawingTopComponent constructor
to accept an InstanceLookup and we add a new no-arg constructor:
private DrawingTopComponent(){
this(new InstanceContent());
}
private DrawingTopComponent(InstanceContent content) {
super( new AbstractLookup( content ) );
content.add( initializePalette() );
initComponents();
setName(NbBundle.getMessage(DrawingTopComponent.class, "CTL_DrawingTopComponent"));
setToolTipText(NbBundle.getMessage(DrawingTopComponent.class, "HINT_DrawingTopComponent"));
// setIcon(Utilities.loadImage(ICON_PATH, true));
}
Re-run the application (Right-click Palette Demo > Install/Reload in
Target Platform). Select Window > Open Drawing Window should also
show the palette.
But what does it do ?
So far our Palette Demo is quite simple. The palette isn't that nice, no icons and it also doesn't do anything.
First, let's add some icons for our palette items and localized text.
Icons
The palette requires two versions of each icon for the palette item. A 16x16 pixel and a 32x32 version.
- Take 2 icons (the 16 and the 32 pixel icons) and add them to your project (this seems amazingly complicated;
just use your favorite file manager to copy the icons in src/com/example/palette). Name the two files
configure-16.png and configure-32.png.
- Edit the simple-item.xml file to configure the palette item with the new icon. The file should look like:
<!DOCTYPE editor_palette_item PUBLIC '-//NetBeans//Editor Palette Item 1.0//EN'
"http://www.netbeans.org/dtds/editor-palette-item-1_0.dtd">
<editor_palette_item>
<icon16 urlvalue="com/example/palette/configure-16.png" />
<icon32 urlvalue="com/example/palette/configure-32.png" />
</editor_palette_item>
Text
- We change the simple-item.xml file some more and add text description
<editor_palette_item>
<icon16 urlvalue="com/example/palette/configure-16.png" />
<icon32 urlvalue="com/example/palette/configure-32.png" />
<description localizing-bundle="com.example.palette.Bundle"
display-name-key="Item_Name"
tooltip-key="Item_Tooltip" />
</editor_palette_item>
Each text should be possible to localize, you define the bundle (localizing-bundle) and then define the
display and tooltip key.
- Since we make a reference in the simple-item.xml to the Bundle, we must edit it to add the new keys.
Open the Bundle.properties file and add the keys:
CTL_DrawingAction=Open Drawing Window
CTL_DrawingTopComponent=Drawing Window
HINT_DrawingTopComponent=This is a Drawing window
OpenIDE-Module-Name=Palette Demo
Item_Name=Item name
Item_Tooltip=A tooltip
If you start the module in the platform, you should see the new icons and text:
Palette item class
Ok, if you looked at the messages in the output window, you would probably see some warnings about the bad
XML format. Each palette item must have besides the icon and text an attached class. The class must implement
ActiveEditorDrop (which assumes a text-only situation but works for non-text data also). The main part
of this class is to be instantiated by the Palette when doing a drag and drop operation. Here it goes:
- Add a new class (File > New File... > Java Classes category > Java Class) with the name MyPaletteItem. It should look like this:
package com.example.palette;
import javax.swing.text.JTextComponent;
import org.openide.text.ActiveEditorDrop;
public class MyPaletteItem implements ActiveEditorDrop{
public boolean handleTransfer(JTextComponent jTextComponent) {
//IGNORE, we don't really care
return true;
}
}
- Configure the palette items with the new class. palette-item.xml should be now:
<editor_palette_item version='1.0'>
<class name="com.example.palette.MyPaletteItem"/>
<icon16 urlvalue="com/example/palette/configure-16.png" />
<icon32 urlvalue="com/example/palette/configure-32.png" />
<description localizing-bundle="com.example.palette.Bundle"
display-name-key="Item_Name"
tooltip-key="Item_Tooltip" />
</editor_palette_item>
Partial summary
So, in order to use the palette we must add to the Lookup of a TopComponent a PaletteController. When creating the
PaletteController, we give to the PaletteFactory the name of a palette folder in the layer.xml.
The folder structure in the layer.xml is quite simple, with subfolders for categories and files for items. Each
item must refer a standard XML file (with a specific Palette DTD). In the palette item XML we define the icons,
the text (via bundle keys) and a corresponding class.
Next
What we need now is a way to:
- Find out which element is selected in the Palette
- Do a drop of a Palette item
Selected element in the Palette
The Palette API allows us to place a listener for changes. Thus we find out the selected object.
- First, we add the UI Utilities API
to the library (Right-click on Palette Demo > Properties >
Libraries > Add.. and select UI Utilities API). It's not mandatory -
that is, you may add the listener without it - but it's nice to show
messages in the status bar.
- We edit the DrawingTopComponent constructor to place our listener:
private DrawingTopComponent(InstanceContent content) {
super( new AbstractLookup( content ) );
final PaletteController controller= initializePalette();
content.add( controller );
controller.addPropertyChangeListener( new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
if( PaletteController.PROP_SELECTED_ITEM.equals( evt.getPropertyName() ) ) {
Lookup selItem = controller.getSelectedItem();
if( null != selItem ) {
ActiveEditorDrop selNode = (ActiveEditorDrop)selItem.lookup( ActiveEditorDrop.class );
if(null != selNode){
StatusDisplayer.getDefault().setStatusText("Selected "+selNode);
}
}
}
}
});
initComponents();
setName(NbBundle.getMessage(DrawingTopComponent.class, "CTL_DrawingTopComponent"));
setToolTipText(NbBundle.getMessage(DrawingTopComponent.class, "HINT_DrawingTopComponent"));
// setIcon(Utilities.loadImage(ICON_PATH, true));
}
Since there are going to be some import problems, a Source > Fix Imports is necessary.
We now have the basic mechanism for our basic Palette implementation. We just need to modify the listener method
and do whatever we need (like change mouse cursor, do an action on next mouse click on our TopComponent, etc).
Drag and drop
The Palette already takes care of the drag part, we just have to provide the drop. (Actually we could also customize
the drag with some custom flavors. Better check the Palette API javadoc if you are in need of such things).
Since we are going to display a message dialog, you need to add the Dialogs API to the module dependencies
(Right-click Palette Demo > Properties > Libraries > Add... > Dialogs API)
Modify the DrawingTopComponent and add the following code
private DrawingTopComponent(InstanceContent content) {
super( new AbstractLookup( content ) );
final PaletteController controller= initializePalette();
content.add( controller );
controller.addPropertyChangeListener( XXX); // removed to save space
setDropTarget(new DropTarget(this,new DropTargetListener() {
public void dragEnter(DropTargetDragEvent dropTargetDragEvent) {
}
public void dragExit(DropTargetEvent dropTargetEvent) {
}
public void dragOver(DropTargetDragEvent dropTargetDragEvent) {
}
public void drop(DropTargetDropEvent dropTargetDropEvent) {
NotifyDescriptor d =new NotifyDescriptor.Message("Drop",
NotifyDescriptor.INFORMATION_MESSAGE);
DialogDisplayer.getDefault().notify(d);
}
public void dropActionChanged(DropTargetDragEvent dropTargetDragEvent) {
}
}));
//the rest removed to save space
That's all there is to it. Our drop method is invoked when necessary and we may use the Palette API for
fun and profit !
Please note that in order to make drop work nicely you should comment the select code (at least the display of
the dialog) since it would interfere with the drop.
Some fun
-
Open the DrawingTopComponent in Design mode, right-click the design window, select Layout and click Null Layout.
- Open it now in Source mode, and modify the drop method:
public void drop(DropTargetDropEvent dropTargetDropEvent) {
JButton j=new JButton("drop");
j.setBounds(dropTargetDropEvent.getLocation().x,
dropTargetDropEvent.getLocation().y,100,20);
add(j);
validate();
repaint();
//accept drop
dropTargetDropEvent.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
}
The code above adds a JButton on the coordinates of the drop.
Versioning
- 26 February 2006 - Initial version
Notes
Tutorial made by Emilian Bold (http://web.info.uvt.ro/~fierarul/typo3/)
The sample code has two icons from the Nuvola set, which is LGPL. Feel free to contact me, as LGPL requires, in
order to get the full set. Hopefully I'll replace those icons with some BSD ones.