Java Solaris Communities Sun Store Join SDN My Profile Why Join?
 
Bug Database
Bug Detail
Quick Lists
Top 25 Bugs
Top 25 RFE's
Recently Closed Bugs
Printable Page Printable Page


Bug Database
Bug ID: 4087846
Votes 31
Synopsis JMenuBar.setHelpMenu() => "not yet implemented"
Category java:classes_swing
Reported Against 1.0.1 , 1.1.6 , 1.2.2 , 1.2fcs , 0.9internal , kestrel-beta
Release Fixed
State 5-Cause Known, request for enhancement
Priority: 3-Medium
Related Bugs 4202314
Submit Date 22-OCT-1997
Description
5. JMenuBar.setHelpMenu() => "not yet implemented"





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
 *   xxxxx@xxxxx  
 * 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" ...]
Posted Date : 2006-01-25 23:19:37.0
Work Around
It is possible to add 'glue' between the help menu and all other menus,
which makes the help menu appear at the right.  Here is a code example:

import java.awt.*;
import com.sun.java.swing.*;

public class GlueTest extends JFrame {

    public GlueTest() {
	JMenuBar mb = new JMenuBar();
	setJMenuBar(mb);
	newMenu(mb);
	newMenu(mb);
	mb.add(Box.createGlue());
	mb.add(new JMenu("Help"));
    }

    public void newMenu(JMenuBar mb) {
	JMenu m = (JMenu)mb.add(new JMenu("File"));
	m.add("Menu item");
	m.add("Menu item");
	m.add("Menu item");
    }

    public static void main(String args[]) {
	GlueTest f = new GlueTest();
	f.pack();
	f.show();
    }
}
Evaluation
This should be added.
Contribution-forum:https://jdk-collaboration.dev.java.net/servlets/ProjectForumMessageView?forumID=1463&messageID=11032
Posted Date : 2006-01-25 23:19:37.0
Comments
  
  Include a link with my name & email   

Submitted On 25-AUG-1998
sbd
The Workaround provides the right appearance,
provided that the Help Menu is added last, or
that other menus are inserted at the correct
position.
I want to add a common Help menu (among other
things) in the constructor of a class A. There
will then be several types of window (B, C and D)
which are all subclasses of A and need to add
their own menus between the FILE menu and the
HELP menu.
Having the JMenuBar maintain a &quot;special&quot; help menu
is just what I need.


Submitted On 25-OCT-1998
alexk
I don't know why this is such a hard feature to
fix.  It's been in swing 0.9 and it's still in
swing 1.1b3.  
Take it out of the API if it doesn't implement it.


Submitted On 27-OCT-1998
sbd
I have written a subclass of JMenuBar which
provides the HelpMenu features. I simply overrode
the getHelpMenu(), setHelpMenu() and add()
methods. I used the workaround above as a guide
(thanks).
The only code change then is to create an instance
of my class instead of a standard JHelpMenu. It
can still be stored and treated everywhere else as
a JHelpMenu.
 


Submitted On 16-MAR-1999
lxhuan
We have the setHelpMenu in API, Why could we have in the swing? As demonstrated
in sbd's example, It easy to implement.


Submitted On 07-DEC-2000
gandolph
Are you kidding??
Not implemented yet message hidden in the code??

Come on!!!


Submitted On 08-OCT-2001
tichyMM
Do not fix this bug! It really made me laugh...

Still ROTFL...


Submitted On 08-OCT-2001
alxs
This bug now is about four years old! J2SE 1.4 will be 
available soon. I can't belive it's still open.


Submitted On 11-OCT-2001
lillywhite
Can this please be implemented? It's ridiculous to document
it in the API for 1.2, 1.3 and 1.4 as "not implemented".


Submitted On 12-JUN-2002
astrogator
That this is not implemented yet, when there exist a workaround, is hard to understand. Shouldn't the method be removed as deprecated at least?


Submitted On 26-SEP-2002
cvenkat_reddy
Test comments


Submitted On 26-SEP-2002
cvenkat_reddy
test comments


Submitted On 29-NOV-2002
gscokart
Please..., that is still not implemented in 1.4.1.  That's still 
ridiculus.
After 5 years, it's still 'In progress...' 


Submitted On 08-OCT-2003
ipreuss
It is also ridiculous to throw *and Error* when the method 
gets called - and to document it in *the second sentence* of 
the API doc, so that it doesn't appear in the overview. In 
fact, it is documented as an Exception instead of an Error... 
<sigh>


Submitted On 08-OCT-2003
gebhard
It is still not implemented in 1.4.2!
Here is a workaround that ensures the help menu to be the 
last menu. It does not care for adding space between the help 
menu and the others. Also the index in add(JMenu, int) is not 
being checked.

import javax.swing.JMenu;
import javax.swing.JMenuBar;

public class HelpImplementedMenuBar extends JMenuBar {
  private JMenu helpMenu;

  public void setHelpMenu(JMenu menu) {
    if (helpMenu != null) {
      remove(helpMenu);
    }
    helpMenu = menu;
    super.add(helpMenu);
  }

  public JMenu add(JMenu menu) {
    if (helpMenu != null) {
      return (JMenu) add(menu, getComponentCount() - 1);
    }
    else {
      return super.add(menu);
    }
  }

  public JMenu getHelpMenu() {
    return helpMenu;
  }

  public void remove(JMenu menu) {
    if (menu == helpMenu) {
      helpMenu = null;
    }
    super.remove(menu);
  }

  public void removeAll() {
    super.removeAll();
    helpMenu = null;
  }
}


Submitted On 09-MAY-2006
8 years later. Still not implemented!


Submitted On 01-APR-2008
trejkaz
10 years and counting. :-D

But on the bright side, Java is open source now.  The change to make setHelpMenu() itself actually work is relatively simple, so any of us could probably submit a patch to the jdk7 project.



PLEASE NOTE: JDK6 is formerly known as Project Mustang