Note: The UGS Platform has been updated to use maven. Some parts of this document need to be updated to reflect that change.

Plugin development

The UGS Platform is built ontop of the NetBeans Platform. This gives us powerful
tools to work with, including a robust plugin system. The heart of the UGS
Platform is a module which wraps and exposes the Universal Gcode Sender JAR
file - the same jar you could execute to run the Classic GUI! Other than using
the UGSLib module, developing a plugin for the UGS Platform is exactly the same
as developing any other NetBeans Platform plugin. And there is lots of great
documentation for that, here is the NetBeans Platform Plugin Quick Start
guide.

Workflow Plugin Tutorial

In this tutorial we're going to build a window to help manage jobs that use
multiple tools which are split into multiple files. The rough design idea will
have a central table with four columns containing:
File name
Tool name (editable)
* Finished flag

There will be a pair of buttons to add and remove files from the table, and
we will also hook up a UGS event listener to detect when files are opened from
other areas of the interface as well.

Lastly, we'll add another pair of buttons to move rows around in the table, so
that we can reorganize the workflow if files were added out of order the order.

Here is a sketch of what we're building:

Create and configure project

Universal Gcode Sender is developed with NetBeans, and plugins are no exception.
Once you've cloned the Universal Gcode Sender project you should be able to open
the UGSPlatform folder with NetBeans and it will discover a project that you can open.
To start building your module expand the UGSPlatform section, right-click the
modules directory and select Add New....

This will open up a wizard where you name the module, and declare the source
path. For this example the module is named WorkflowModuleTutorial and the source
path is com.willwinder.ugs.nbp.workflowmoduletutorial which is the convention
used in the core modules.

Add UGS dependencies

Your module should now be listed in the Modules section. If it doesn't you may
need to restart NetBeans. Before we dive into the code there are a couple helper
classes to import which will give you full access to the UGS API. Double click
your module from the Modules section to open the code, then right-click the top
level item which appeared and select the properties menu.

Select Add Dependency..., here you should search for UGSLib and
CentralLookup then add them to your plugin.

Create window class

Now we're ready to build the module. In this tutorial we're building a window
to manage a multi-job workflow, so we'll start by adding a window to customize.

Open the new module and right click the new package, in the context menu go to
New -> Window.... To bring up the new window wizard.

In the first screen of the wizard choose the default location your window
will appear. Custom locations have been designed for UGS Platform, the largest
is named visualizer because it is the Visualizer's default location. We'll
use this location for our plugin. This means that when our plugin opens it will
be tabbed with the Visualizer module.

Click next and choose a class name for your module, for this tutorial I'm
going to call it WorkflowModuleTutorial.

Build the GUI

The NetBeans GUI builder makes it easy to make a custom user interface without
writing a single line of code (which is the main reason UGS uses NetBeans!).
Using the GUI builder we'll add some buttons and a table. This step can be as
elaborate as you want. If you're a seasoned swing developer and prefer not to
use the magic GUI builder, no worries, you can create the UI programatically as
well - but that is a different tutorial.

Take a look at the screenshot below. The [TopComponent] - Navigator - Editor
window shows all the objects that have been added with the GUI builder.

There are four JButtons, a JTable nested inside a JScrollPane and a JPanel
which I used to make alignment a little easier (The GUI Builder is powerful,
but it can also be a bit quirky).

Putting the JTable inside a JScrollPane makes it so that if too many items
get added to the table it will scroll rather than dissapear off the bottom.

Note: The name given to these components will be used in the code, so
be sure to use the names shown in the screenshot.

The JTable is going to be the trickiest part of build the GUI. To configure the
table right-click the JTable object from the component navigator and select
Table Contents.... Here you can add our 3 columns and specify that the data
types. You can also specify which columns are editable, in this example we want
the user to be able to type in what type of tool should be used.

Autogenerated code

Before writing any code, lets take a look at what has already been automatically
generated for us.

Just above the class there are a number of annotations. These are used by
the NetBeans platform, most of them were setup according to how you filled
in the Wizards earlier. They can also configure things like keyboard
shortcuts, and where things are put in the dropdown menus.

Within the class there are several grayed out sections. This is code
generated by NetBeans which the IDE prevents you from modifying outside the
GUI builder or in some cases component properties. For example if you wanted
to use a custom JTable, you would configure the table in the GUI builder by
adding a custom constructor.

At the end of the file is componentOpened and componentClosed, these are
lifecycle events that are called when the window has been opened or closed.

Also at the end of the file is writeProperties and readProperties, these
are used to save the window state between runs.

Annotated code

This is the longest section because it will explain every line of code added to
the WorkflowModuleTutorial class. The most complicated code deals with Swing
component manipulation, with just a smattering of UGS lifecycle events to push
things along.

Class signature

First there are a few class state object we'll need and two Listeners we'll
be implementing.

/**
* UGSEventListener - this is how a plugin can listen to UGS lifecycle events.
* ListSelectionListener - listen for table selections.
*/
public final class WorkflowWindowTutorialTopComponent
extends TopComponent
implements UGSEventListener, ListSelectionListener {
// These are the UGS backend objects for interacting with the backend.
private final Settings settings;
private final BackendAPI backend;
// This is used to identify when a stream has completed.
private boolean wasSending;
// This is used in most methods, so cache it here.
DefaultTableModel model;

Constructor

In the constructor we register the class with the UGS backend and also set
the class as a listener to table selection events.

File Complete Handler

When a command is complete we'll update the JTable, select the next file that
needs to be sent and popup a notification informing the user what they should
do next. The selection event will be sent and handled in the selection
handler.

Add/Remove Button Action Handlers

Now we implement the button event methods. They are generated by double
clicking the buttons in the GUI Builder. This generates the swing code that
attaches the ActionPerformed events to the button click callbacks.

removeButtonActoinPerformed is even simpler, it uses standard JTable
functionality to remove any selected rows. The only thing clever here is that
rows are removed starting from the end to avoid having the index of later
selections change while deleting rows one at a time.

Up / Down Button Action Handlers

The up and down action buttons are pure java code. They don't do anything
you wouldn't do with any other Swing application. The code here deals strictly
with moving selections around. Although a little tricky, and not totally
relevant to UGS, they are included because the feature wouldn't be complete
without them.

NetBeans Platform Component Lifecycle Code

Of the automatically generated methods, componentOpened is the only one
which needed some custom code. In case the component had been closed earlier
or wasn't loaded until after a file stream started, grab the wasSending
state and save it for later.