bb.gui
Class LinePanel

java.lang.Object
  extended by java.awt.Component
      extended by java.awt.Container
          extended by javax.swing.JComponent
              extended by javax.swing.JPanel
                  extended by bb.gui.LinePanel
All Implemented Interfaces:
ImageObserver, MenuContainer, Serializable, Accessible

public class LinePanel
extends JPanel

A Swing container that resembles the AWT container Box: it always lays out its Components in a single line (that can be either horizontal or vertical), and it does not allow the LayoutManager to be changed from what it was constructed with.

Besides being an AWT widget, Box is not as easy to use as it could be. In particular, it usually requires the user to add a Border to it to effect spacing with its parent Container. Likewise, it requires the user to add explicit "glue", "strut" or "rigid areas" Components inside it to effect spacing and sizing among its contents.

In contrast, this class is always configured to automatically insert gaps between Components that touch the edge of the parent Container, as well as gaps between Components inside it. Consequently, usage is trivial: an instance is created via one of the factory methods, and then you simply add those Components you really care about to it. For example, here is how you can create a Container that holds a horizontal row of buttons:


        JButton jbutton1 = new JButton("jbutton1");
        JButton jbutton2 = new JButton("jbutton2");
                // then do whatever configuration is need on those buttons, like add ActionListeners

                // can easily create a Container to hold all those buttons as follows:
        LinePanel linePanel = LinePanel.makeHorizontal();
        linePanel.add( jbutton1 );
        linePanel.add( jbutton2 );
 

Some layout issues have been seen with this class.

The first issue is that have seen instances get shrunk much more than seems reasonable. This class currently uses GroupLayout for its LayoutManager, and this issue seems to only happen when this class is used in a GUI with other Containers that have different LayoutManagers. It appears to be caused by older LayoutManagers or Components doing screwy things with preferred and maximum sizes. The cure is to always use this class, or at least always use GroupLayout (or some other modern LayoutManager?) in other Containers.

The second issue that have seen is sometimes a JTextArea embedded inside a JScrollPane will stretch far more than it ought to (e.g. to the point where the scroll bars are outside the window). This only seems to happen when the text gets really large (in either rows or columns). This is a bug with GroupLayout (see this bug report as well as my JScrollPaneDemo class in my singleShotPrograms project). One cure is to limit the preferred size of the JScrollPane with code like this:


        private JComponent buildTextArea(String text) {
                JTextArea jtextArea = new JTextArea();
                //jtextArea.setXXX(...);        // do whatever configuration is needed

                JScrollPane jscrollPane = new JScrollPane(jtextArea);
                jscrollPane.setPreferredSize( SwingUtil.screenSize() ); // CRITICAL: if do not call this, have seen the JScrollPane get made WAY bigger than the window, which is bad, since cannot see the scroll bars plus other components get pushed off screen
                return jscrollPane;
        }
 

Like typical Java GUI code, this class is not multithread safe: it expects to only be called by EventQueue's dispatch thread. This threading limitation is checked in every public method.

Author:
Brent Boyer
See Also:
Serialized Form

Nested Class Summary
static class LinePanel.Axis
          Enum of the possible orientations of the axis that components will be laid out along.
static class LinePanel.UnitTest
          See the Overview page of the project's javadocs for a general description of this unit test class.
 
Nested classes/interfaces inherited from class javax.swing.JPanel
JPanel.AccessibleJPanel
 
Nested classes/interfaces inherited from class javax.swing.JComponent
JComponent.AccessibleJComponent
 
Nested classes/interfaces inherited from class java.awt.Container
Container.AccessibleAWTContainer
 
Nested classes/interfaces inherited from class java.awt.Component
Component.AccessibleAWTComponent, Component.BaselineResizeBehavior, Component.BltBufferStrategy, Component.FlipBufferStrategy
 
Field Summary
private  LinePanel.Axis axis
          Contract: is never null.
private  GroupLayout groupLayout
          Contract: is never null.
private  GroupLayout.Group groupParallel
          Contract: is never null.
private  GroupLayout.Group groupSequential
          Contract: is never null.
private static long serialVersionUID
           
 
Fields inherited from class javax.swing.JComponent
accessibleContext, listenerList, TOOL_TIP_TEXT_KEY, ui, UNDEFINED_CONDITION, WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, WHEN_FOCUSED, WHEN_IN_FOCUSED_WINDOW
 
Fields inherited from class java.awt.Component
BOTTOM_ALIGNMENT, CENTER_ALIGNMENT, LEFT_ALIGNMENT, RIGHT_ALIGNMENT, TOP_ALIGNMENT
 
Fields inherited from interface java.awt.image.ImageObserver
ABORT, ALLBITS, ERROR, FRAMEBITS, HEIGHT, PROPERTIES, SOMEBITS, WIDTH
 
Constructor Summary
LinePanel(LinePanel.Axis axis, boolean isDoubleBuffered)
          Constructs a new LinePanel with the specified LinePanel.Axis and buffering strategy.
 
Method Summary
protected  void addImpl(Component comp, Object constraints, int index)
           
private  void initLayout()
           
static LinePanel makeHorizontal()
          Returns a new LinePanel with a horizontal GroupLayout layout manager and a double buffer.
static LinePanel makeVertical()
          Returns a new LinePanel with a vertical GroupLayout layout manager and a double buffer.
private  void readObject(ObjectInputStream ois)
          Customizes deserialization.
 void setLayout(LayoutManager mgr)
          Overrides the superclass version to forbid changing the LayoutManager after construction.
 
Methods inherited from class javax.swing.JPanel
getAccessibleContext, getUI, getUIClassID, paramString, setUI, updateUI
 
Methods inherited from class javax.swing.JComponent
addAncestorListener, addNotify, addVetoableChangeListener, computeVisibleRect, contains, createToolTip, disable, enable, firePropertyChange, firePropertyChange, firePropertyChange, fireVetoableChange, getActionForKeyStroke, getActionMap, getAlignmentX, getAlignmentY, getAncestorListeners, getAutoscrolls, getBaseline, getBaselineResizeBehavior, getBorder, getBounds, getClientProperty, getComponentGraphics, getComponentPopupMenu, getConditionForKeyStroke, getDebugGraphicsOptions, getDefaultLocale, getFontMetrics, getGraphics, getHeight, getInheritsPopupMenu, getInputMap, getInputMap, getInputVerifier, getInsets, getInsets, getListeners, getLocation, getMaximumSize, getMinimumSize, getNextFocusableComponent, getPopupLocation, getPreferredSize, getRegisteredKeyStrokes, getRootPane, getSize, getToolTipLocation, getToolTipText, getToolTipText, getTopLevelAncestor, getTransferHandler, getVerifyInputWhenFocusTarget, getVetoableChangeListeners, getVisibleRect, getWidth, getX, getY, grabFocus, isDoubleBuffered, isLightweightComponent, isManagingFocus, isOpaque, isOptimizedDrawingEnabled, isPaintingForPrint, isPaintingTile, isRequestFocusEnabled, isValidateRoot, paint, paintBorder, paintChildren, paintComponent, paintImmediately, paintImmediately, print, printAll, printBorder, printChildren, printComponent, processComponentKeyEvent, processKeyBinding, processKeyEvent, processMouseEvent, processMouseMotionEvent, putClientProperty, registerKeyboardAction, registerKeyboardAction, removeAncestorListener, removeNotify, removeVetoableChangeListener, repaint, repaint, requestDefaultFocus, requestFocus, requestFocus, requestFocusInWindow, requestFocusInWindow, resetKeyboardActions, reshape, revalidate, scrollRectToVisible, setActionMap, setAlignmentX, setAlignmentY, setAutoscrolls, setBackground, setBorder, setComponentPopupMenu, setDebugGraphicsOptions, setDefaultLocale, setDoubleBuffered, setEnabled, setFocusTraversalKeys, setFont, setForeground, setInheritsPopupMenu, setInputMap, setInputVerifier, setMaximumSize, setMinimumSize, setNextFocusableComponent, setOpaque, setPreferredSize, setRequestFocusEnabled, setToolTipText, setTransferHandler, setUI, setVerifyInputWhenFocusTarget, setVisible, unregisterKeyboardAction, update
 
Methods inherited from class java.awt.Container
add, add, add, add, add, addContainerListener, addPropertyChangeListener, addPropertyChangeListener, applyComponentOrientation, areFocusTraversalKeysSet, countComponents, deliverEvent, doLayout, findComponentAt, findComponentAt, getComponent, getComponentAt, getComponentAt, getComponentCount, getComponents, getComponentZOrder, getContainerListeners, getFocusTraversalKeys, getFocusTraversalPolicy, getLayout, getMousePosition, insets, invalidate, isAncestorOf, isFocusCycleRoot, isFocusCycleRoot, isFocusTraversalPolicyProvider, isFocusTraversalPolicySet, layout, list, list, locate, minimumSize, paintComponents, preferredSize, printComponents, processContainerEvent, processEvent, remove, remove, removeAll, removeContainerListener, setComponentZOrder, setFocusCycleRoot, setFocusTraversalPolicy, setFocusTraversalPolicyProvider, transferFocusBackward, transferFocusDownCycle, validate, validateTree
 
Methods inherited from class java.awt.Component
action, add, addComponentListener, addFocusListener, addHierarchyBoundsListener, addHierarchyListener, addInputMethodListener, addKeyListener, addMouseListener, addMouseMotionListener, addMouseWheelListener, bounds, checkImage, checkImage, coalesceEvents, contains, createImage, createImage, createVolatileImage, createVolatileImage, disableEvents, dispatchEvent, enable, enableEvents, enableInputMethods, firePropertyChange, firePropertyChange, firePropertyChange, firePropertyChange, firePropertyChange, firePropertyChange, getBackground, getBounds, getColorModel, getComponentListeners, getComponentOrientation, getCursor, getDropTarget, getFocusCycleRootAncestor, getFocusListeners, getFocusTraversalKeysEnabled, getFont, getForeground, getGraphicsConfiguration, getHierarchyBoundsListeners, getHierarchyListeners, getIgnoreRepaint, getInputContext, getInputMethodListeners, getInputMethodRequests, getKeyListeners, getLocale, getLocation, getLocationOnScreen, getMouseListeners, getMouseMotionListeners, getMousePosition, getMouseWheelListeners, getName, getParent, getPeer, getPropertyChangeListeners, getPropertyChangeListeners, getSize, getToolkit, getTreeLock, gotFocus, handleEvent, hasFocus, hide, imageUpdate, inside, isBackgroundSet, isCursorSet, isDisplayable, isEnabled, isFocusable, isFocusOwner, isFocusTraversable, isFontSet, isForegroundSet, isLightweight, isMaximumSizeSet, isMinimumSizeSet, isPreferredSizeSet, isShowing, isValid, isVisible, keyDown, keyUp, list, list, list, location, lostFocus, mouseDown, mouseDrag, mouseEnter, mouseExit, mouseMove, mouseUp, move, nextFocus, paintAll, postEvent, prepareImage, prepareImage, processComponentEvent, processFocusEvent, processHierarchyBoundsEvent, processHierarchyEvent, processInputMethodEvent, processMouseWheelEvent, remove, removeComponentListener, removeFocusListener, removeHierarchyBoundsListener, removeHierarchyListener, removeInputMethodListener, removeKeyListener, removeMouseListener, removeMouseMotionListener, removeMouseWheelListener, removePropertyChangeListener, removePropertyChangeListener, repaint, repaint, repaint, resize, resize, setBounds, setBounds, setComponentOrientation, setCursor, setDropTarget, setFocusable, setFocusTraversalKeysEnabled, setIgnoreRepaint, setLocale, setLocation, setLocation, setName, setSize, setSize, show, show, size, toString, transferFocus, transferFocusUpCycle
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
 

Field Detail

serialVersionUID

private static final long serialVersionUID
See Also:
Constant Field Values

axis

private final LinePanel.Axis axis
Contract: is never null.


groupLayout

private transient GroupLayout groupLayout
Contract: is never null.


groupParallel

private transient GroupLayout.Group groupParallel
Contract: is never null.


groupSequential

private transient GroupLayout.Group groupSequential
Contract: is never null.

Constructor Detail

LinePanel

public LinePanel(LinePanel.Axis axis,
                 boolean isDoubleBuffered)
          throws IllegalArgumentException,
                 IllegalStateException
Constructs a new LinePanel with the specified LinePanel.Axis and buffering strategy.

Parameters:
axis - specifes the direction that components will be laid out along
isDoubleBuffered - a boolean, true for double-buffering, which uses additional memory space to achieve fast, flicker-free updates
Throws:
IllegalArgumentException - if axis is null or is not a recognized value
IllegalStateException - if calling thread is not EventQueue's dispatch thread
Method Detail

makeHorizontal

public static LinePanel makeHorizontal()
                                throws IllegalStateException
Returns a new LinePanel with a horizontal GroupLayout layout manager and a double buffer.

Throws:
IllegalStateException - if calling thread is not EventQueue's dispatch thread

makeVertical

public static LinePanel makeVertical()
                              throws IllegalStateException
Returns a new LinePanel with a vertical GroupLayout layout manager and a double buffer.

Throws:
IllegalStateException - if calling thread is not EventQueue's dispatch thread

initLayout

private void initLayout()
                 throws IllegalArgumentException
Throws:
IllegalArgumentException

readObject

private void readObject(ObjectInputStream ois)
                 throws IllegalStateException,
                        ClassNotFoundException,
                        IOException,
                        NotActiveException
Customizes deserialization.

Background: the sole issue is that Sun screwed up and failed to make GroupLayout implement Serializable. This class's GroupLayout-related fields are all transient, so no problem here. Unfortunately, our superlass, JPanel, retains a reference to the layout manager and we have no control over its serialization.

The solution is that we use a GroupLayout2 for groupLayout, since it is serializable in some sense. Well, GroupLayout2 does not actually write or read any data during serialization, but at least it does not throw any Exceptions. This enables serialization of this class to proceed without modification (which is why there is no implementation of writeObject).

To deserialize here, this method first calls ObjectInputStream.defaultReadObject(), which restores all of this class's non-transient state as well as its superclass's (including all the components that were added). Then, since it has its axis field available, it only need call initLayout to recreate the GroupLayout-related fields.

Throws:
IllegalStateException - if calling thread is not EventQueue's dispatch thread
ClassNotFoundException - if the class of a serialized object could not be found
IOException - if an I/O problem occurs
NotActiveException

addImpl

protected void addImpl(Component comp,
                       Object constraints,
                       int index)
Overrides:
addImpl in class Container

setLayout

public void setLayout(LayoutManager mgr)
               throws IllegalStateException
Overrides the superclass version to forbid changing the LayoutManager after construction.

Overrides:
setLayout in class Container
Throws:
IllegalStateException - if construction has finished ()