JMRI: Use of Swing
Author: Bob Jacobsen
Keywords: JMRI technical code standards recommendation Swing

# JMRI Code: Use of Swing

Note: This page describes a toolkit for creating JMRI's GUI that's been slowly developed as part of JMRI. Please use this if you're creating new JMRI user interfaces. Note that there are proposals to move JMRI to other, non-specific toolkits in this area. Before you do work to extend or improve this toolkit itself, please ask on jmri-developers about that status of that work.

We use Java Swing for our GUI development. It's a lot more powerful than the original AWT, and the price is right. In particular, we try to use the "Bean format" of setting and getting members, call-backs to notify of changes, etc, to make it easier to build applications from JMRI components.

## General Principles

We have been evolving a particular pattern for using Swing, described here. The JMRI codebase contains several generations of implementations, so not all of it looks like this, but we're moving classes in this direction as time allows.

The basic structure is:

- Keep Swing code in packages with `swing` in the package path. For example, prefer putting Swing code in `jmri.jmrit.vsdecoder.swing` or a subpackage of that, instead of putting it in `jmri.jmrit.vsdecoder` itself. This helps keep the other code non-Swing-specific, e.g. so it can be used with other toolkits or on systems without graphics. This pattern is similar to the way that ConfigureXml code lives in separate `.configurexml` packages.
- Implement graphical tools as JmriPanel objects. These are JPanels with enough extra structure that the JMRI applications can directly work with them. For example, a JmriPanel subclass can be instantiated and placed in a properly laid out window by creating a JmriNamedPanel action with just the name of the JmriPanel class, which in turn can be done with various automated tools.
  Do not create JmriJFrame or JFrame subclasses with lots of specific function
  This pattern lets us write a tool panel just once, and then use it in lots of various places, embedded into windows in several ways. It also greatly reduces the number of classes that need to be loaded at startup time, because there are not separate *Action and *Frame classes, and JmriPanel subclasses don't have to be loaded just because they are listed in a menu.
- Use the Swing and AWT native support for decoding events. Do not write your own code for decode clicks, mouse down, drags or other user interactions; use the Swing classes for that. For example, use `MouseAdapter` as a way of getting events for mouse pressed, mouse clicked, and mouse released. These will differ on different platforms and with different hardware, and it extremely unlikely that any code you write will do a better job of decoding all that.

## JMRI Pattern for Swing Window Creation

The jmri.util.swing package contains the support code.

### Life Cycle of a JmriPanel

First the ctor runs, then initComponents. That second part should be the place for connections to other components, as all lower level objects have been created. (subclasses for particular systems might have e.g. more initComponents methods, called later)

Dispose is called at the end. (Note that JPanels don't have a dispose(), that's normally only part of JFrames, but we provide it here for cleanup)

### Displaying a JmriPanel

JmriPanels are best created by name with JmriNamedPaneAction, which has the advantage of greatly reducing the number of classes that need to be loaded to populate a menu.

To create an action, e.g. for a menu item, the simplest form is:

          new jmri.util.swing.JmriNamedPaneAction("Log4J Tree", "jmri.jmrit.log.Log4JTreePane");

The first argument is the human-readable name, and the 2nd is the name of the panel class.

An example of a fuller form:

          new jmri.util.swing.JmriNamedPaneAction(Bundle.getMessage("MenuItemLogTreeAction"),

                    new jmri.util.swing.sdi.JmriJFrameInterface(),

                    "jmri.jmrit.log.Log4JTreePane");

- The first argument is the human-readable name for the action, e.g. what will show in the menu or on the button. That's internationalized here by using Bundle.
- The second argument is the context in which to display it, which in this case is a new plain window. (See below for more info on the options here)
- And the third argument is the name of the specific JmriPanel class to be instantiated and used when the action is invoked. The class isn't loaded until first used, because we've put a String name here, which saves a bunch of time at startup for large menus.

See the JmriNamedPaneAction Javadoc for more information.

If you need specialized initialization that can't be built into the JmriPanel itself via it's `initComponents` and `initContext(..)` methods, perhaps to make decision about connections, make a specialized Action class by extending `jmri.util.swing.JmriNamedPaneAction`, providing the appropriate constructors, and including a `@Override public JmriPanel makePanel()` method that does any case-specific initialization that's needed before the panel can be used. For an example (may have been changed) see jmri.jmrix.loconet.swing.LnNamedPaneAction.

If none that can be used, look into using JmriAbstractAction as the base for a separate class implementing Action.

### Menus, ToolsBars, Buttons, etc

If you're using JmriPanels as described above, JMRI also provides tools for creating menus, toolbars, button fields, etc more easily.

Generic creation of menus, toolbars and navigation trees from XML definition files are provided by the jmri.util.swing.JMenuUtil, jmri.util.swing.JToolBarUtil, and jmri.util.swing.JTreeUtil classes

I18N of those menus, toolbars and trees is then done via the XML content in the usual way.

## Window Control

JMRI provides three different ways of embedding JmriPanels in windows:

- The jmri.util.swing.sdi package provides the traditional JMRI "single-document interface", where there are multiple independent windows
- The jmri.util.swing.multipane package provides a "multi-pane" or IDE-style interface", where each window is tiled with inter-related panes. This is used for the (new) DecoderPro.
- The jmri.util.swing.mdi package provides a "multi-document interface", where a primary window contains multiple independent sub-windows. This currently isn't used by a JMRI app, at least not as far as we know, but it's available if wanted.

Each of those then provides an implementation of WindowInterface that creates new windows, subwindows or other constructs as needed, so as to put panels in the right place.

(See the jmri.util.swing package Javadocs for more information

## Misc

(This section needs more organization)

JmriJFrame is in the wrong package for historical reasons, will eventually move to jmri.util.swing.

Older classes, some still to be moved to jmri.util.swing, some are 1.1.8 adapters that should just go away.

- AbstractFrameAction
- BusyGlassPane
- FileChooserFilter
- FontUtil
- JTableUtil
- JmriInsets
- JmriJFrame
- JmriJFrameAction
- JmriSpinner
- MouseInputAdapterInstaller
- ResizableImagePanel
- WindowMenu