5. JMenuBar.setHelpMenu() => "not yet implemented"
Name: krT82822 Date: 09/27/99
The JMenuBar class is documented as having a setHelpMenu method.
This method is used to place the Help menu of an application on the right hand side of the menubar. This method is documented as unimplemented. The help menu is considered special in other windowing systems, e.g. Motif. Can we have this implemented please? I can't be that difficult to do.
(Review ID: 95784)
======================================================================
Suggested fix by java.net member leouser:
A DESCRIPTION OF THE FIX :
BUGID:4087846 JMenuBar.setHelpMenu() => "not yet implemented"
FILES AFFECTED: javax.swing.JMenuBar, javax.swing.plaf.basic.BasicMenuUI
JDK VERSION
jdk-6-rc-bin-b64-linux-i586-15_dec_2005.bin
Discusion(embeded in test case as well):
/**
* BUGID:4087846 JMenuBar.setHelpMenu() => "not yet implemented"
* This bug/rfe has been sitting out there for 9 years! The problem is
* that to solve this without adding a mountain of convoluted code to
* the JMenuBar class you must define a layout to deal with the special
* HelpMenu, which is what Ive done. By defining it as a layout problem
* and not a management of the Container array of Components we free ourselves
* of instrumenting all the adds, all the removes, etc... . All these
* methods retain their meaning and the owner of the HelpMenu remains
* the Containers array. We hold a WeakReference to it in the JMenuBar
* so the JMenuBar does not retain a fake 'ownership' reference, allowing
* us to easily move it to another parent or even get rid of it if we want
* to. There just isn't a 'special' component marker system in place within
* the Container class to do what we need at this level.
*
* Defining it as a layout problem, as stated, makes it a matter of the
* layout deciding where it wants to put the darn thing. The DefaultMenuLayout
* is so close to what we want to do that I broke the 'dont copy and paste'
* code rule and created a variation of the BoxLayout inside of the
* BasicMenuBarUI class. This I felt had to be done, we want BoxLayout behavior
* with a little bit of sorting added to ensure that the HelpMenu is the
* last component. BoxLayout does not offer enough hooks in its API to make
* what we needed to do possible. If it is decided that the BoxLayout isn't
* quite right, we can reimplement it to do something different. The cut and
* pastiness of it is my one major gripe I have with this, otherwise I like
* this solution. Define HelpMenu as a layout problem and it becomes smooth
* sailing.
*
* ANTI-RATIONALE: See my cut and paste gripe. We shouldn't worry about
* method collision with a subclass since these methods have always existed.
*
* TESTING STRATEGY:
* Create some JMenuBars, exercise the help method menus and see that the
* HelpMenu is layed out at the end of each displayed JMenuBar.
*
* FILES AFFECTED: javax.swing.JMenuBar, javax.swing.plaf.basic.BasicMenuUI
*
* JDK VERSION
* jdk-6-rc-bin-b64-linux-i586-15_dec_2005.bin
*
* test ran succesfully on a SUSE 7.3 Linux distribution
*
* Brian Harry
* ###@###.###
* Jan 25, 2006
*/
UNFIED DIFFS:
--- /home/nstuff/java6/jdk1.6.0/javax/swing/JMenuBar.java Thu Dec 15 02:17:36 2005
+++ /home/javarefs/javax/swing/JMenuBar.java Wed Jan 25 11:45:36 2006
@@ -14,6 +14,7 @@
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.*;
+import java.lang.ref.WeakReference;
import java.util.Vector;
import java.util.Enumeration;
@@ -74,6 +75,7 @@
private boolean paintBorder = true;
private Insets margin = null;
+ private WeakReference<JMenu> helpMenu = null;
/* diagnostic aids -- should be false for production builds. */
private static final boolean TRACE = false; // trace creates and disposes
@@ -199,23 +201,40 @@
/**
* Sets the help menu that appears when the user selects the
- * "help" option in the menu bar. This method is not yet implemented
- * and will throw an exception.
+ * "help" option in the menu bar.
*
* @param menu the JMenu that delivers help to the user
*/
public void setHelpMenu(JMenu menu) {
- throw new Error("setHelpMenu() not yet implemented.");
+ if(helpMenu != null){
+ JMenu old = helpMenu.get();
+ if(old != null && old.getParent() == this)
+ remove(old);
+ helpMenu = null;
+ }
+
+ if(menu != null){
+ helpMenu = new WeakReference(menu);
+ add(menu);
+ }
}
/**
- * Gets the help menu for the menu bar. This method is not yet
- * implemented and will throw an exception.
+ * Gets the help menu for the menu bar.
*
* @return the <code>JMenu</code> that delivers help to the user
*/
public JMenu getHelpMenu() {
- throw new Error("getHelpMenu() not yet implemented.");
+ JMenu help = null;
+ if(helpMenu != null){
+ JMenu tmphelp = helpMenu.get();
+ if(tmphelp != null && tmphelp.getParent() == this)
+ help = tmphelp;
+ }
+
+ if(helpMenu != null && help == null)
+ helpMenu = null;
+ return help;
}
/**
--- /home/nstuff/java6/jdk1.6.0/javax/swing/plaf/basic/BasicMenuBarUI.java Thu Dec 15 02:17:44 2005
+++ /home/javarefs/javax/swing/plaf/basic/BasicMenuBarUI.java Wed Jan 25 10:31:56 2006
@@ -11,17 +11,23 @@
import sun.swing.UIAction;
import javax.swing.*;
import javax.swing.event.*;
+import java.awt.AWTError;
import java.awt.Color;
import java.awt.Component;
+import java.awt.ComponentOrientation;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
+import java.awt.LayoutManager2;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Comparator;
import javax.swing.border.*;
import javax.swing.plaf.*;
@@ -62,7 +68,7 @@
protected void installDefaults() {
if (menuBar.getLayout() == null ||
menuBar.getLayout() instanceof UIResource) {
- menuBar.setLayout(new DefaultMenuLayout(menuBar,BoxLayout.LINE_AXIS));
+ menuBar.setLayout(new JMenuBarLayout(menuBar,BoxLayout.LINE_AXIS));
}
LookAndFeel.installProperty(menuBar, "opaque", Boolean.TRUE);
@@ -218,6 +224,377 @@
}
}
}
+
+ /**
+ * Very similiar to the {@code BoxLayout} in function, the {@code JMenuBarLayout}
+ * lays out its components so that the HelpMenu is always layed out at
+ * the tail end of the layout. It does not matter if it was the first
+ * component added to the JMenuBar, or the 2nd, or 3rd, etc... .
+ * It is always last.
+ */
+ public static class JMenuBarLayout implements LayoutManager2, Serializable {
+
+ /**
+ * Specifies that components should be laid out left to right.
+ */
+ public static final int X_AXIS = 0;
+
+ /**
+ * Specifies that components should be laid out top to bottom.
+ */
+ public static final int Y_AXIS = 1;
+
+ /**
+ * Specifies that components should be laid out in the direction of
+ * a line of text as determined by the target container's
+ * <code>ComponentOrientation</code> property.
+ */
+ public static final int LINE_AXIS = 2;
+
+ /**
+ * Specifies that components should be laid out in the direction that
+ * lines flow across a page as determined by the target container's
+ * <code>ComponentOrientation</code> property.
+ */
+ public static final int PAGE_AXIS = 3;
+
+ /**
+ * Creates a layout manager that will lay out components along the
+ * given axis.
+ *
+ * @param target the container that needs to be laid out
+ * @param axis the axis to lay out components along. Can be one of:
+ * <code>BoxLayout.X_AXIS</code>,
+ * <code>BoxLayout.Y_AXIS</code>,
+ * <code>BoxLayout.LINE_AXIS</code> or
+ * <code>BoxLayout.PAGE_AXIS</code>
+ *
+ * @exception <code>AWTError</code> if the value of <code>axis</code> is invalid
+ */
+ public JMenuBarLayout(JMenuBar target, int axis){
+ this.axis = axis;
+ jmb = target;
+ }
+
+ /**
+ * Indicates that a child has changed its layout related information,
+ * and thus any cached calculations should be flushed.
+ * <p>
+ * This method is called by AWT when the invalidate method is called
+ * on the Container. Since the invalidate method may be called
+ * asynchronously to the event thread, this method may be called
+ * asynchronously.
+ *
+ * @param target the affected container
+ *
+ * @exception {@code AWTError} if the target isn't the container specified to the
+ * {@code JMenuBarLayout} constructor
+ */
+ public synchronized void invalidateLayout(Container target) {
+ checkContainer(target);
+ xChildren = null;
+ yChildren = null;
+ xTotal = null;
+ yTotal = null;
+ }
+
+ /**
+ * Not used by this class.
+ *
+ * @param name the name of the component
+ * @param comp the component
+ */
+ public void addLayoutComponent(String name, Component comp) {
+ invalidateLayout(comp.getParent());
+ }
+
+ /**
+ * Not used by this class.
+ *
+ * @param comp the component
+ */
+ public void removeLayoutComponent(Component comp) {
+ invalidateLayout(comp.getParent());
+ }
+
+ /**
+ * Not used by this class.
+ *
+ * @param comp the component
+ * @param constraints constraints
+ */
+ public void addLayoutComponent(Component comp, Object constraints) {
+ invalidateLayout(comp.getParent());
+ }
+
+ /**
+ * Returns the preferred dimensions for this layout, given the components
+ * in the specified target container.
+ *
+ * @param target the container that needs to be laid out
+ * @return the dimensions >= 0 && <= Integer.MAX_VALUE
+ * @exception {@code AWTError} if the target isn't the container specified to the
+ * {@code JMenuBarLayout} constructor
+ * @see Container
+ * @see #minimumLayoutSize
+ * @see #maximumLayoutSize
+ */
+ public Dimension preferredLayoutSize(Container target) {
+ Dimension size;
+ synchronized(this) {
+ checkContainer(target);
+ checkRequests(target.getComponents());
+ size = new Dimension(xTotal.preferred, yTotal.preferred);
+ }
+
+ Insets insets = target.getInsets();
+ size.width = (int) Math.min((long) size.width + (long) insets.left + (long) insets.right, Integer.MAX_VALUE);
+ size.height = (int) Math.min((long) size.height + (long) insets.top + (long) insets.bottom, Integer.MAX_VALUE);
+ return size;
+ }
+
+ /**
+ * Returns the minimum dimensions needed to lay out the components
+ * contained in the specified target container.
+ *
+ * @param target the container that needs to be laid out
+ * @return the dimensions >= 0 && <= Integer.MAX_VALUE
+ * @exception {@code AWTError} if the target isn't the container specified to the
+ * {@code JMenuBarLayout} constructor
+ * @see #preferredLayoutSize
+ * @see #maximumLayoutSize
+ */
+ public Dimension minimumLayoutSize(Container target) {
+ Dimension size;
+ synchronized(this) {
+ checkContainer(target);
+ checkRequests(target.getComponents());
+ size = new Dimension(xTotal.minimum, yTotal.minimum);
+ }
+
+ Insets insets = target.getInsets();
+ size.width = (int) Math.min((long) size.width + (long) insets.left + (long) insets.right, Integer.MAX_VALUE);
+ size.height = (int) Math.min((long) size.height + (long) insets.top + (long) insets.bottom, Integer.MAX_VALUE);
+ return size;
+ }
+
+ /**
+ * Returns the maximum dimensions the target container can use
+ * to lay out the components it contains.
+ *
+ * @param target the container that needs to be laid out
+ * @return the dimenions >= 0 && <= Integer.MAX_VALUE
+ * @exception {@code AWTError} if the target isn't the container specified to the
+ * {@code JMenuBarLayout} constructor
+ * @see #preferredLayoutSize
+ * @see #minimumLayoutSize
+ */
+ public Dimension maximumLayoutSize(Container target) {
+ Dimension size;
+ synchronized(this) {
+ checkContainer(target);
+ checkRequests(target.getComponents());
+ size = new Dimension(xTotal.maximum, yTotal.maximum);
+ }
+
+ Insets insets = target.getInsets();
+ size.width = (int) Math.min((long) size.width + (long) insets.left + (long) insets.right, Integer.MAX_VALUE);
+ size.height = (int) Math.min((long) size.height + (long) insets.top + (long) insets.bottom, Integer.MAX_VALUE);
+ return size;
+ }
+
+ /**
+ * Returns the alignment along the X axis for the container.
+ * If the box is horizontal, the default
+ * alignment will be returned. Otherwise, the alignment needed
+ * to place the children along the X axis will be returned.
+ *
+ * @param target the container
+ * @return the alignment >= 0.0f && <= 1.0f
+ * @exception {@code AWTError} if the target isn't the container specified to the
+ * {@code JMenuBarLayout} constructor
+ */
+ public synchronized float getLayoutAlignmentX(Container target) {
+ checkContainer(target);
+ checkRequests(target.getComponents());
+ return xTotal.alignment;
+ }
+
+ /**
+ * Returns the alignment along the Y axis for the container.
+ * If the box is vertical, the default
+ * alignment will be returned. Otherwise, the alignment needed
+ * to place the children along the Y axis will be returned.
+ *
+ * @param target the container
+ * @return the alignment >= 0.0f && <= 1.0f
+ * @exception {@code AWTError} if the target isn't the container specified to the
+ * {@code JMenuBarLayout} constructor
+ */
+ public synchronized float getLayoutAlignmentY(Container target) {
+ checkContainer(target);
+ checkRequests(target.getComponents());
+ return yTotal.alignment;
+ }
+
+ /**
+ * Called by the AWT <!-- XXX CHECK! --> when the specified container
+ * needs to be laid out.
+ *
+ * @param target the container to lay out
+ *
+ * @exception {@code AWTError} if the target isn't the container specified to the
+ * {@code JMenuBarLayout} constructor
+ */
+ public void layoutContainer(Container target) {
+ checkContainer(target);
+ Component[] children = jmb.getComponents();
+ if(jmb.getHelpMenu() != null)
+ Arrays.sort(children, new HMenuComparator(jmb.getHelpMenu()));
+ int[] xOffsets = new int[children.length];
+ int[] xSpans = new int[children.length];
+ int[] yOffsets = new int[children.length];
+ int[] ySpans = new int[children.length];
+
+ Dimension alloc = jmb.getSize();
+ Insets in = jmb.getInsets();
+ alloc.width -= in.left + in.right;
+ alloc.height -= in.top + in.bottom;
+
+ // Resolve axis to an absolute value (either X_AXIS or Y_AXIS)
+ ComponentOrientation o = jmb.getComponentOrientation();
+ int absoluteAxis = resolveAxis( axis, o );
+ boolean ltr = (absoluteAxis != axis) ? o.isLeftToRight() : true;
[ snip ... too large to include the rest ... Refer to attached file "634034.txt" ...]