Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-6499857

Synopsis: JMenuItem.getRootPane() returns null

    XMLWordPrintable

    Details

      Description

      FULL PRODUCT VERSION :
      java version "1.6.0-beta2"
      Java(TM) SE Runtime Environment (build 1.6.0-beta2-b84)
      Java HotSpot(TM) Client VM (build 1.6.0-beta2-b84, mixed mode, sharing)

      java version "1.5.0_06"
      Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_06-b05)
      Java HotSpot(TM) Client VM (build 1.5.0_06-b05, mixed mode, sharing)


      ADDITIONAL OS VERSION INFORMATION :
      Microsoft Windows XP [Version 5.1.2600]


      A DESCRIPTION OF THE PROBLEM :
      The javadocs for JComponent.getRootPane states that it returns "the
      JRootPane that contains this component, or null if no JRootPane is
      found." However, when I add a JMenuItem to a JMenu, its
      getRootPane() method always returns null, even though it is clearly
      contained by the same JRootPane as its menu. The reason for this is
      that it doesn't strictly belong to the JMenu, it belongs to the
      JMenu's JPopupMenu. However, I often need to get the root pane of a
      JMenuItem. In the example here, I need the rootpane to set a wait
      cursor, which is a very standard operation. (In my actual code, the
      JMenuItem code is much farther removed from the container class, so
      I can't simply use a reference to it, like I could here.) But the
      call to getRootPane returns null, and the ActionListener doesn't
      have access to any component in the heirarchy other than the
      JMenuItem. I need to get the root pane from the menu item. Notice
      that the actionListener is written to work fine from a toolbar
      button as well, and it does, although this test case doesn't bother
      to demonstrate that. Let me also say that, even though I have a
      useful workaround, this should somehow be incorporated into the
      standard JMenuItem, because developers shouldn't have to wrestle
      with this issue. The getRootPane() method is very useful and
      powerful, and it's relatively simple to make it work in a JMenuItem.
      It makes no sense that it should be an exception to the rule.


      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Run this test program. On the file menu, try both the broken and
      fixed menu items. Then try the same things for each submenu. Also,
      bring up the popup menu in the window and try both menu items
      there, and on its submenu.


      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      Both the "broken" and "fixed" menu items use the same
      ActionListener, and should open a file-open dialog.

      ACTUAL -
      Only the "Fixed" menu item opens the file-open dialog. The "Broken"
      item throws a NullPointerException.


      ERROR MESSAGES/STACK TRACES THAT OCCUR :
      getRootPane() returns null, so I get a NullPointerException as soon
      as I try to use it.


      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      import java.awt.BorderLayout;
      import java.awt.Cursor;
      import java.awt.event.*;
      import javax.swing.*;

      @SuppressWarnings({"HardCodedStringLiteral", "MagicNumber"})
      public class RootPaneBug extends JPanel {
      private static final Cursor WAIT_CURSOR = Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR);
      private static final Cursor DEFAULT_CURSOR = Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);

      public static void main(String[] args) {
      //noinspection StringConcatenation
      System.err.println("Java Version " + System.getProperty("java.version")); // NON-NLS
      makeFrame();
      }

      private static void makeFrame() {
      JFrame mainFrame = new JFrame("RootPaneBug");
      mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
      RootPaneBug comp = new RootPaneBug();
      mainFrame.add(comp, BorderLayout.CENTER);
      comp.setUpMenus();
      mainFrame.setBounds(10, 10, 600, 325);
      mainFrame.setVisible(true);
      }

      public RootPaneBug() {
      super(new BorderLayout());
      }

      private void setUpMenus() {
      JMenu fileMenu = new JMenu("File");
      addOpenItems(fileMenu);
      JMenu subMenu = new JMenu("Open SubMenu");
      addOpenItems(subMenu);
      fileMenu.add(subMenu);
      JMenu subSubMenu = new JMenu("Open Next Submenu");
      addOpenItems(subSubMenu);
      subMenu.add(subSubMenu);
      JMenuBar mBar = new JMenuBar();
      mBar.add(fileMenu);
      ((JFrame)getRootPane().getParent()).setJMenuBar(mBar);
      final JPopupMenu popupMenu = new JPopupMenu();
      addOpenItems(popupMenu);
      JMenu popupSubMenu = new JMenu("Popup Submenu");
      addOpenItems(popupSubMenu);
      popupMenu.add(popupSubMenu);
      MouseListener ml = new MouseAdapter() {
      @Override public void mouseClicked(MouseEvent e) { showPopup(e); }
      @Override public void mousePressed(MouseEvent e) { showPopup(e); }
      @Override public void mouseReleased(MouseEvent e) { showPopup(e); }

      private void showPopup(MouseEvent e) {
      if (e.isPopupTrigger()) {
      popupMenu.setInvoker(RootPaneBug.this);
      popupMenu.setLocation(e.getLocationOnScreen());
      popupMenu.setVisible(true);
      }
      }
      };
      addMouseListener(ml);
      }

      private void addOpenItems(JComponent pFileMenu) {
      JMenuItem brokenOpenItem = new JMenuItem("Broken Open...");
      JMenuItem fixedOpenItem = new FixedMenuItem("Fixed Open...");
      pFileMenu.add(brokenOpenItem);
      pFileMenu.add(fixedOpenItem);
      ActionListener al = new ActionListener() {
      public void actionPerformed(ActionEvent e) {
      JComponent menuItem = (JComponent) e.getSource();
      JRootPane rootPane = menuItem.getRootPane();
      rootPane.setCursor(WAIT_CURSOR);
      try {
      new JFileChooser().showOpenDialog(rootPane);
      } finally {
      rootPane.setCursor(DEFAULT_CURSOR);
      }
      }
      };
      brokenOpenItem.addActionListener(al);
      fixedOpenItem.addActionListener(al);
      }
      }

      @SuppressWarnings({"ClassNameDiffersFromFileName"})
      class FixedMenuItem extends JMenuItem {
      public FixedMenuItem(String text) {
      super(text);
      }

      @Override public JRootPane getRootPane() {
      return getMenuItemRootPane(this);
      }

      public static JRootPane getMenuItemRootPane(JMenuItem cmp) {
      JComponent invoker = ((JComponent) ((JPopupMenu) cmp.getParent()).getInvoker());
      // I need to call this recursively in case this is a submenu.
      return getRootPaneUniversally(invoker);
      }

      /**
      * The JComponent.getRootPane() method fails for a JMenuItem. So we don't
      * call it directly. Instead, we call this method, which works with any JComponent.
      * @param cmp The component whose root pane you need.
      * @return The root pane for the specified component.
      */
      public static JRootPane getRootPaneUniversally(JComponent cmp) {
      JRootPane rootPane;
      if (cmp instanceof JMenuItem) {
      // This may return a valid root pane if the JMenuItem is a JMenu but not a submenu
      rootPane = SwingUtilities.getRootPane(cmp);
      if (rootPane == null) {
      rootPane = getMenuItemRootPane((JMenuItem)cmp);
      }
      } else {
      rootPane = SwingUtilities.getRootPane(cmp);
      }
      return rootPane;
      }
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      The workaround is contained in the FixedMenuItem class contained in
      the submitted source code.

        Attachments

          Issue Links

            Activity

              People

              Assignee:
              Unassigned Unassigned
              Reporter:
              tyao Ting-Yun Ingrid Yao (Inactive)
              Votes:
              0 Vote for this issue
              Watchers:
              0 Start watching this issue

                Dates

                Created:
                Updated:
                Imported:
                Indexed: