NetBeans Platform 6.9: Working with Actions

4 min read

(For more resources on NetBeans, see here.)

In Swing, an Action object provides an ActionListener for Action event handling, together with additional features, such as tool tips, icons, and the Action’s activated state. One aim of Swing Actions is that they should be reusable, that is, can be invoked from a menu item as well as a related toolbar button and keyboard shortcut.

The NetBeans Platform provides an Action framework enabling you to organize Actions declaratively. In many cases, you can simply reuse your existing Actions exactly as they were before you used the NetBeans Platform, once you have declared them. For more complex scenarios, you can make use of specific NetBeans Platform Action classes that offer the advantages of additional features, such as more complex displays in toolbars and support for context-sensitive help.

Preparing to work with global actions

Before you begin working with global Actions, let’s make some changes to our application. It should be possible for the TaskEditorTopComponent to open for a specific task. You should therefore be able to pass a task into the TaskEditorTopComponent. Rather than the TaskEditorPanel creating a new task in its constructor, the task needs to be passed into it and made available to the TaskEditorTopComponent.

On the other hand, it may make sense for a TaskEditorTopComponent to create a new task, rather than providing an existing task, which can then be made available for editing. Therefore, the TaskEditorTopComponent should provide two constructors. If a task is passed into the TaskEditorTopComponent, the TaskEditorTopComponent and the TaskEditorPanel are initialized. If no task is passed in, a new task is created and is made available for editing.

Furthermore, it is currently only possible to edit a single task at a time. It would make sense to be able to work on several tasks at the same time in different editors. At the same time, you should make sure that the task is only opened once by the same editor. The TaskEditorTopComponent should therefore provide a method for creating new or finding existing editors. In addition, it would be useful if TaskEditorPanels were automatically closed for deleted tasks.

  1. Remove the logic for creating new tasks from the constructor of the TaskEditorPanel, along with the instance variable for storing the TaskManager, which is now redundant:

    public TaskEditorPanel() {
    this.pcs = new PropertyChangeSupport(this);

  2. Introduce a new method to update a task:

    public void updateTask(Task task) {
    Task oldTask = this.task;
    this.task = task;
    this.pcs.firePropertyChange(PROP_TASK, oldTask, this.task);

  3. Let us now turn to the TaskEditorTopComponent, which currently cannot be instantiated either with or without a task being provided. You now need to be able to pass a task for initializing the TaskEditorPanel. The new default constructor creates a new task with the support of a chained constructor, and passes this to the former constructor for the remaining initialization of the editor.
  4. In addition, it should now be able to return several instances of the TaskEditorTopComponent that are each responsible for a specific task. Hence, the class should be extended by a static method for creating new or finding existing instances. These instances are stored in a Map<Task, TaskEditorTopComponent> which is populated by the former constructor with newly created instances. The method checks whether the map for the given task already stores a responsible instance, and creates a new one if necessary. Additionally, this method registers a Listener on the TaskManager to close the relevant editor for deleting a task. As an instance is now responsible for a particular task this should be able to be queried, so we introduce another appropriate method. Consequently, the changes to the TaskEditorTopComponent looks as follows:

private static Map<Task, TaskEditorTopComponent> tcByTask =
new HashMap<Task, TaskEditorTopComponent>();

public static TaskEditorTopComponent findInstance(Task task) {
TaskEditorTopComponent tc = tcByTask.get(task);
if (null == tc) {
tc = new TaskEditorTopComponent(task);
if (null == taskMgr) {
taskMgr = Lookup.getDefault().lookup(TaskManager.class);
return tc;

private class ListenForRemovedNodes implements PropertyChangeListener {
public void propertyChange(PropertyChangeEvent arg0) {
(arg0.getPropertyName())) {
Task task = (Task) arg0.getNewValue();
TaskEditorTopComponent tc = tcByTask.get(task);
if (null != tc) {

private TaskEditorTopComponent() {

private TaskEditorTopComponent(TaskManager taskMgr) {
this((taskMgr != null) ? taskMgr.createTask() : null);

private TaskEditorTopComponent(Task task) {

// ...

((TaskEditorPanel) this.jPanel1).updateTask(task);
this.ic.add(((TaskEditorPanel) this.jPanel1).task);
this.associateLookup(new AbstractLookup(this.ic));
tcByTask.put(task, this);

public String getTaskId() {
Task task = ((TaskEditorPanel) this.jPanel1).task;
return (null != task) ? task.getId() : "";

With that our preparations are complete and you can turn to the following discussion on Actions.


Please enter your comment!
Please enter your name here