---
category: swing
folder: PopupWithoutClickOnMenu
title: JMenuの領域内にマウスカーソルでポップアップメニューを表示する
tags: [JMenu, MouseListener]
author: aterai
pubdate: 2013-02-18T00:29:59+09:00
description: JMenuの領域内にマウスカーソルが入ったときにポップアップメニューが開くように設定します。
image: https://lh3.googleusercontent.com/-shu8CDTfLvg/USCnbrWYstI/AAAAAAAABd0/qODgUmweras/s800/PopupWithoutClickOnMenu.png
---
* 概要 [#summary]
`JMenu`の領域内にマウスカーソルが入ったときにポップアップメニューが開くように設定します。

#download(https://lh3.googleusercontent.com/-shu8CDTfLvg/USCnbrWYstI/AAAAAAAABd0/qODgUmweras/s800/PopupWithoutClickOnMenu.png)

* サンプルコード [#sourcecode]
#code(link){{
visitAll(menubar, new MouseAdapter() {
  @Override public void mousePressed(MouseEvent e) {
    if (check.isSelected()) {
      ((AbstractButton) e.getComponent()).doClick();
    }
  }

  @Override public void mouseEntered(MouseEvent e) {
    if (check.isSelected()) {
      ((AbstractButton) e.getComponent()).doClick();
    }
  }
});
}}

* 解説 [#explanation]
上記のサンプルでは、トップレベルの(`JMenuBar`の子コンポーネントになっている)`JMenu`の領域内にマウスカーソルが入った場合に自動的にポップアップメニューが開くように、`JMenu#doClick()`を実行する`MouseListener`を追加しています。

- マウスボタンを押した場合も入った場合にすでに表示したポップアップメニューが非表示にならないように`JMenu#doClick()`を実行
- このサンプルのすべての`JMenuItem`は、`beep`音を鳴らすだけのダミー
- このサンプルのすべての`JMenuItem`は`beep`音を鳴らすだけのアクションを設定している
- この`JMenu`の入った、`JPopupMenu`を`JComponent#setComponentPopupMenu(...)`で`JMenuBar`以外のコンポーネントに設定すると無限ループする
-- [https://bugs.openjdk.org/browse/JDK-6949414 Bug ID: JDK-6949414 JMenu.buildMenuElementArray() endless loop]
- 回避方法:
-- マウスイベントを作成して`menu.dispatchEvent(new MouseEvent(menu, MouseEvent.MOUSE_ENTERED, e.getWhen(), 0, 0, 0, 0, false));`を実行する
--- [https://stackoverflow.com/questions/25260684/programmatically-expand-sub-jmenuitems java - Programmatically expand sub JMenuItems - Stack Overflow]
-- `MenuElement`の配列を作成し、`MenuSelectionManager.defaultManager().setSelectedPath(new MenuElement[]{...});`を実行する
--- ドキュメントには、[https://docs.oracle.com/javase/jp/8/docs/api/javax/swing/MenuSelectionManager.html#setSelectedPath-javax.swing.MenuElement:A- 「このメソッドは public ですが、Look & Feel エンジンで使用されるため、クライアントアプリケーションからは呼び出さないでください。」]と記述されているが、現状では`JMenu`の`buildMenuElementArray(...)`が以下の状態なので仕方ない

#code{{
/*
 * Build an array of menu elements - from <code>PopupMenu</code> to
 * the root <code>JMenuBar</code>.
 * @param  leaf  the leaf node from which to start building up the array
 * @return the array of menu items
 */
private MenuElement[] buildMenuElementArray(JMenu leaf) {
    Vector<MenuElement> elements = new Vector<MenuElement>();
    Component current = leaf.getPopupMenu();
    JPopupMenu pop;
    JMenu menu;
    JMenuBar bar;

    while (true) {
        if (current instanceof JPopupMenu) {
            pop = (JPopupMenu) current;
            elements.insertElementAt(pop, 0);
            current = pop.getInvoker();
        } else if (current instanceof JMenu) {
            menu = (JMenu) current;
            elements.insertElementAt(menu, 0);
            current = menu.getParent();
        } else if (current instanceof JMenuBar) {
            bar = (JMenuBar) current;
            elements.insertElementAt(bar, 0);
            MenuElement me[] = new MenuElement[elements.size()];
            elements.copyInto(me);
            return me;
        }
    }
}
}}

* 参考リンク [#reference]
- [http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=9327&forum=12 JMenuBarの動作 - Java Solution - @IT]
- [https://stackoverflow.com/questions/12125402/activate-jmenubar-on-hover java - Activate JMenuBar on hover - Stack Overflow]

* コメント [#comment]
#comment
#comment