QJCC homepage

biz.chitec.quarterback.swing
Class DynamicMenuBar

java.lang.Object
  extended byjava.awt.Component
      extended byjava.awt.Container
          extended byjavax.swing.JComponent
              extended byjavax.swing.JMenuBar
                  extended bybiz.chitec.quarterback.swing.DynamicMenuBar
All Implemented Interfaces:
javax.accessibility.Accessible, DefaultLocaleChangeReceiver, java.awt.image.ImageObserver, java.awt.MenuContainer, javax.swing.MenuElement, java.io.Serializable

public abstract class DynamicMenuBar
extends javax.swing.JMenuBar
implements DefaultLocaleChangeReceiver

Dynamic menu bar definition for "living" menu bars. Well, this is not one of those boring every-day menu bars with some JMenuItem() and JMenu() definitions which are stuck together statically one by one. The dynamic menu bar is highly dynamic and the code behind this is in this class.

To understand this code means to understand its basic design principle: The menu structure is defined not by Swing components, but by a metastructure which produces the actual components on request. Which components are produced and whether they are enabled or not depends on the current client's state. This has to be evaluated by the descendants of this class and can include an arbitrary number of client states. Each time one of those input parameters changes, either the complete menu bar is rebuilt or the enable state of the elements is changed.

The components of the menu-describing metastructure are of the type MenuElement. Its core method reflects the event described above: MenuElement#getElements(Integer) delivers the elements produced by this element container. Certain MenuElement descendants may contain other MenuElements so that the complete structure builds a tree which follows the actual menu structure later seen in the menu bar.

For a MenuElement to decide whether has to deliver components or not, a LogicExpr is used. Apart from the default combining expressions (AND, OR,...), a number of logic expressions is defined within the DynamicMenuBar. Implementations will usually define more such expressions which evaluate the already mentioned program state. Generally speaking, if the expression evaluates to true, the menu element delivers its Swing elements, otherwise it does not.

The code breaks into six parts:

  1. Intro with field definitions and constructor start
  2. Definition of the menu elements meta tree. These are more than 300 lines of Java code without a single semicolon...
  3. Business logic. The #build(Integer) method reshapes the element tree, #propertyChange(PropertyChangeEvent) listenes on events which change the menu bar and DynamicMenuBar.MenuMaker orders the steps in a way that multiple change events lead to only one change of the tree.
  4. MenuElements. All the different types of MenuElements as inner classes. They are used in the tree definition part above.
  5. Logic expressions. They are also used in the tree definition. Note that we declare a base class which grounds everything we do not need from the LogicExpr.
  6. Actions. While some of the more specific actions are defined as anonymous members directly in the menu element tree, those which are used several times are declared here.
The menu bar knows about locale changes during runtime. The implementation is currently rather clumsy, however. See defaultLocaleChanged() for more details.

History

This is the generalized implementation of a multi-year development in a cantamen-internal software. Finally, the code came to a point where it is generally useable and therefore moved into the QJCC.

Version:
$Id: 5b99f92bb93dafcf676daf024c1cd8cddc536878 $
Author:
cantamen/Dirk Hillbrecht 2003-2007
See Also:
Serialized Form

Nested Class Summary
protected  class DynamicMenuBar.ActionMenuElement
          Simple menu element which is driven by an Action
protected  class DynamicMenuBar.GroupMenuElement
          Group multiple elements behind a common LogicExpr.
protected  class DynamicMenuBar.MenuElement
          Logical menu element.
private  class DynamicMenuBar.MenuMaker
          Extra management class for delayed operation.
protected  class DynamicMenuBar.SeparatorMenuElement
          Seperator in a menu.
protected  class DynamicMenuBar.SimpleExpr
           
protected  class DynamicMenuBar.SimpleMenuMenuElement
          Default menu which contains other menu elements.
 
Nested classes inherited from class javax.swing.JMenuBar
javax.swing.JMenuBar.AccessibleJMenuBar
 
Nested classes inherited from class javax.swing.JComponent
javax.swing.JComponent.AccessibleJComponent
 
Nested classes inherited from class java.awt.Container
java.awt.Container.AccessibleAWTContainer
 
Nested classes inherited from class java.awt.Component
java.awt.Component.AccessibleAWTComponent, java.awt.Component.BltBufferStrategy, java.awt.Component.FlipBufferStrategy
 
Field Summary
protected static java.lang.Integer ENABLEDMODE
           
protected  Hotkeys hotkeymanager
           
private  boolean localchanged
           
private  DynamicMenuBar.MenuElement menuelements
           
private  DynamicMenuBar.MenuMaker menumaker
           
protected  java.util.ResourceBundle rb
           
protected static java.lang.Integer VISIBLEMODE
           
 
Fields inherited from class javax.swing.JMenuBar
 
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.Container
 
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
DynamicMenuBar()
           
DynamicMenuBar(java.util.ResourceBundle otherrb)
           
 
Method Summary
 void build()
           
 void checkEnabled()
           
 void defaultLocaleChanged()
          Inform the menu bar that the default locale has changed.
private  void doBuild()
          CALLED ONLY VIA MenuMaker!!!
private  void doCheckEnabled()
          CALLED ONLY VIA MenuMaker!!!
protected abstract  DynamicMenuBar.MenuElement[] getMenuDefinition()
           
 
Methods inherited from class javax.swing.JMenuBar
add, addNotify, getAccessibleContext, getComponent, getComponentAtIndex, getComponentIndex, getHelpMenu, getMargin, getMenu, getMenuCount, getSelectionModel, getSubElements, getUI, getUIClassID, isBorderPainted, isSelected, menuSelectionChanged, paintBorder, paramString, processKeyBinding, processKeyEvent, processMouseEvent, removeNotify, setBorderPainted, setHelpMenu, setMargin, setSelected, setSelectionModel, setUI, updateUI
 
Methods inherited from class javax.swing.JComponent
addAncestorListener, addPropertyChangeListener, addPropertyChangeListener, addVetoableChangeListener, computeVisibleRect, contains, createToolTip, disable, enable, firePropertyChange, firePropertyChange, firePropertyChange, firePropertyChange, firePropertyChange, firePropertyChange, firePropertyChange, firePropertyChange, firePropertyChange, fireVetoableChange, getActionForKeyStroke, getActionMap, getAlignmentX, getAlignmentY, getAncestorListeners, getAutoscrolls, getBorder, getBounds, getClientProperty, getComponentGraphics, getConditionForKeyStroke, getDebugGraphicsOptions, getDefaultLocale, getGraphics, getHeight, getInputMap, getInputMap, getInputVerifier, getInsets, getInsets, getListeners, getLocation, getMaximumSize, getMinimumSize, getNextFocusableComponent, getPreferredSize, getPropertyChangeListeners, getPropertyChangeListeners, getRegisteredKeyStrokes, getRootPane, getSize, getToolTipLocation, getToolTipText, getToolTipText, getTopLevelAncestor, getTransferHandler, getVerifyInputWhenFocusTarget, getVetoableChangeListeners, getVisibleRect, getWidth, getX, getY, grabFocus, isDoubleBuffered, isLightweightComponent, isManagingFocus, isMaximumSizeSet, isMinimumSizeSet, isOpaque, isOptimizedDrawingEnabled, isPaintingTile, isPreferredSizeSet, isRequestFocusEnabled, isValidateRoot, paint, paintChildren, paintComponent, paintImmediately, paintImmediately, print, printAll, printBorder, printChildren, printComponent, processComponentKeyEvent, processKeyEvent, processMouseMotionEvent, putClientProperty, registerKeyboardAction, registerKeyboardAction, removeAncestorListener, removePropertyChangeListener, removePropertyChangeListener, removeVetoableChangeListener, repaint, repaint, requestDefaultFocus, requestFocus, requestFocus, requestFocusInWindow, requestFocusInWindow, resetKeyboardActions, reshape, revalidate, scrollRectToVisible, setActionMap, setAlignmentX, setAlignmentY, setAutoscrolls, setBackground, setBorder, setDebugGraphicsOptions, setDefaultLocale, setDoubleBuffered, setEnabled, setFont, setForeground, 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, addImpl, applyComponentOrientation, areFocusTraversalKeysSet, countComponents, deliverEvent, doLayout, findComponentAt, findComponentAt, getComponent, getComponentAt, getComponentAt, getComponentCount, getComponents, getContainerListeners, getFocusTraversalKeys, getFocusTraversalPolicy, getLayout, insets, invalidate, isAncestorOf, isFocusCycleRoot, isFocusCycleRoot, isFocusTraversalPolicySet, layout, list, list, locate, minimumSize, paintComponents, preferredSize, printComponents, processContainerEvent, processEvent, remove, remove, removeAll, removeContainerListener, setFocusCycleRoot, setFocusTraversalKeys, setFocusTraversalPolicy, setLayout, 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, getBackground, getBounds, getColorModel, getComponentListeners, getComponentOrientation, getCursor, getDropTarget, getFocusCycleRootAncestor, getFocusListeners, getFocusTraversalKeysEnabled, getFont, getFontMetrics, getForeground, getGraphicsConfiguration, getHierarchyBoundsListeners, getHierarchyListeners, getIgnoreRepaint, getInputContext, getInputMethodListeners, getInputMethodRequests, getKeyListeners, getLocale, getLocation, getLocationOnScreen, getMouseListeners, getMouseMotionListeners, getMouseWheelListeners, getName, getParent, getPeer, getSize, getToolkit, getTreeLock, gotFocus, handleEvent, hasFocus, hide, imageUpdate, inside, isBackgroundSet, isCursorSet, isDisplayable, isEnabled, isFocusable, isFocusOwner, isFocusTraversable, isFontSet, isForegroundSet, isLightweight, 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, processMouseEvent, processMouseWheelEvent, remove, removeComponentListener, removeFocusListener, removeHierarchyBoundsListener, removeHierarchyListener, removeInputMethodListener, removeKeyListener, removeMouseListener, removeMouseMotionListener, removeMouseWheelListener, 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

VISIBLEMODE

protected static final java.lang.Integer VISIBLEMODE

ENABLEDMODE

protected static final java.lang.Integer ENABLEDMODE

rb

protected java.util.ResourceBundle rb

hotkeymanager

protected Hotkeys hotkeymanager

localchanged

private boolean localchanged

menumaker

private DynamicMenuBar.MenuMaker menumaker

menuelements

private DynamicMenuBar.MenuElement menuelements
Constructor Detail

DynamicMenuBar

public DynamicMenuBar(java.util.ResourceBundle otherrb)

DynamicMenuBar

public DynamicMenuBar()
Method Detail

getMenuDefinition

protected abstract DynamicMenuBar.MenuElement[] getMenuDefinition()

build

public void build()

checkEnabled

public void checkEnabled()

doBuild

private void doBuild()
CALLED ONLY VIA MenuMaker!!!


doCheckEnabled

private void doCheckEnabled()
CALLED ONLY VIA MenuMaker!!!


defaultLocaleChanged

public void defaultLocaleChanged()
Inform the menu bar that the default locale has changed. Once the locale changes, the menu bar will have to call TOM.defaultLocaleChanged(Component) on itself for the rest of its lifetime. Reason: TOM can only follow the Swing element tree. Therefore, it only sees those menus and menu items which are actually active. If other menu items become visible lateron, their locale has not been changed. This can only happen by TOM walking again through the item tree.

It is not even sufficient to call TOM only if the current locale is different from the original locale. Menues and menu items are instantiated lazily so it is not clear what their original locale was. As it is neither clear whether they have been changed by the last locale change or somewhen else, the only solution is to check their locale every time the tree is rebuilt.

To prevent this extra computation step, the MenuElements would need knowledge about the locale problem. MenuElements which need to do so could ask TOM manually to change their content and an additional method would walk to the tree. For the sake of simplicity, this has not been implemented so far.

Specified by:
defaultLocaleChanged in interface DefaultLocaleChangeReceiver

QJCC homepage