---
category: swing
folder: MenuItemAcceleratorAlignment
title: JMenuItemのAccelerator表示を右揃えにする
tags: [JMenuItem, Locale, KeyEvent, ResourceBundle, Alignment, GridBagLayout]
author: aterai
pubdate: 2013-09-30T05:06:53+09:00
description: JMenuItemのAccelerator表示を右揃えに変更します。
image: https://lh5.googleusercontent.com/-vl8nnt_tGvQ/UkhDq1YV-VI/AAAAAAAAB2o/B0-hGdv35Ns/s800/MenuItemAcceleratorAlignment.png
hreflang:
    href: https://java-swing-tips.blogspot.com/2014/10/jmenuitem-accelerator-text-alignment.html
    lang: en
---
* 概要 [#summary]
`JMenuItem`の`Accelerator`表示を右揃えに変更します。

#download(https://lh5.googleusercontent.com/-vl8nnt_tGvQ/UkhDq1YV-VI/AAAAAAAAB2o/B0-hGdv35Ns/s800/MenuItemAcceleratorAlignment.png)

* サンプルコード [#sourcecode]
#code(link){{
public static void paintAccText(
  Graphics g, MenuItemLayoutHelper lh, MenuItemLayoutHelper.LayoutResult lr,
  Color disabledForeground, Color acceleratorForeground,
  Color acceleratorSelectionForeground) {
  if (!lh.getAccText().equals("")) {
    ButtonModel model = lh.getMenuItem().getModel();
    g.setFont(lh.getAccFontMetrics().getFont());
    if (!model.isEnabled()) {
      // *** paint the accText disabled
      if (disabledForeground != null) {
        g.setColor(disabledForeground);
        SwingUtilities2.drawString(
          lh.getMenuItem(), g, lh.getAccText(),
          lr.getAccRect().x,
          lr.getAccRect().y + lh.getAccFontMetrics().getAscent());
      } else {
        g.setColor(lh.getMenuItem().getBackground().brighter());
        SwingUtilities2.drawString(
          lh.getMenuItem(), g, lh.getAccText(),
          lr.getAccRect().x,
          lr.getAccRect().y + lh.getAccFontMetrics().getAscent());
        g.setColor(lh.getMenuItem().getBackground().darker());
        SwingUtilities2.drawString(
          lh.getMenuItem(), g, lh.getAccText(),
          lr.getAccRect().x - 1,
          lr.getAccRect().y + lh.getFontMetrics().getAscent() - 1);
      }
    } else {
      // *** paint the accText normally
      if (model.isArmed() ||
          (lh.getMenuItem() instanceof JMenu && model.isSelected())) {
        g.setColor(acceleratorSelectionForeground);
      } else {
        g.setColor(acceleratorForeground);
      }
      SwingUtilities2.drawString(
        lh.getMenuItem(), g, lh.getAccText(),
        // lr.getAccRect().x, >>>
        lh.getViewRect().x + lh.getViewRect().width
        - lh.getMenuItem().getIconTextGap() - lr.getAccRect().width,
        // <<<
        lr.getAccRect().y + lh.getAccFontMetrics().getAscent());
    }
  }
}
}}

* 解説 [#explanation]
- `Default`
-- デフォルトでは、`WindowsLookAndFeel`の場合でも`Accelerator`は左寄せで表示される
-- デフォルトの`Windows`環境の場合`Accelerator`は左寄せ
-- デフォルトの`Ubuntu`環境の場合`Accelerator`は右寄せ
- `RightAcc`
-- `BasicMenuItemUI#paintMenuItem(...)`メソッドをオーバーライドして、`JMenuItem`の`Accelerator`を左寄せではなく右寄せで表示
-- `Web Start`で実行すると`java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "accessClassInPackage.sun.swing")`と例外が発生してメニューが表示されない
-- `sun.swing.MenuItemLayoutHelper`や、`sun.swing.MenuItemLayoutHelper.LayoutResult`などの内部所有の`API`を使用しているので、今後も使用できるか不明
-- `paintText(...)`、`paintCheckIcon(...)`、`paintIcon(...)`、`paintArrowIcon(...)`などの`BasicMenuItemUI`のプライベートなメソッドをほぼそのままコピーして使用している
-- `ComponentOrientation.RIGHT_TO_LEFT`が設定されて、文字列が右から左に配置される場合は考慮していない

----
- `EastAcc`
-- `Windows 7`環境の`WindowsLookAndFeel`でのみ正常に動作?
-- `JMenuItem`に`GridBagLayout`を設定し、`c.anchor = GridBagConstraints.EAST;`で、`Accelerator`文字列を設定した`JLabel`を配置
-- 本来の`Accelerator`文字列は、`UIManager.put("MenuItem.acceleratorForeground", background);`などで背景色と同化して非表示
-- 参考: [[JMenuItemの内部にJButtonを配置する>Swing/ButtonsInMenuItem]]
#code{{
private static JMenuItem makeMenuItem2(JMenuItem mi) {
  final JLabel label = new JLabel(MenuItemUIHelper.getAccText(mi, "+"));
  label.setOpaque(true);
  JMenuItem item = new JMenuItem(mi.getText()) {
    @Override public void updateUI() {
      super.updateUI();
      if (getUI() instanceof WindowsMenuItemUI) {
        setUI(new WindowsMenuItemUI() {
          @Override protected void installDefaults() {
            super.installDefaults();
            acceleratorForeground = UIManager.getColor("MenuItem.background");
            acceleratorSelectionForeground = acceleratorForeground;
          }
        });
      }
    }
  };

  GridBagConstraints c = new GridBagConstraints();
  item.setLayout(new GridBagLayout());
  c.gridheight = 1;
  c.gridwidth  = 1;
  c.gridy = 0;
  c.gridx = 0;
  c.insets = new Insets(0, 0, 0, 4);

  c.weightx = 1d;
  c.fill = GridBagConstraints.HORIZONTAL;
  item.add(Box.createHorizontalGlue(), c);
  c.gridx = 1;
  c.fill = GridBagConstraints.NONE;
  c.weightx = 0d;
  c.anchor = GridBagConstraints.EAST;
  item.add(label, c);

  item.setMnemonic(mi.getMnemonic());
  item.setAccelerator(mi.getAccelerator());
  return item;
}
}}

----
- `JMenuItem`の`Accelerator`が`JMenuItem#setLocale(Locale.ENGLISH)`としても変化しない
-- [https://bugs.openjdk.java.net/browse/JDK-6292739 Bug ID: JDK-6292739 Locale change at runtime doesn't affect text displayed for accelerator keys]
- `JDK 1.7.0`から`Locale.getLocale()`が`Locale.JAPAN`などの場合、`KeyEvent#getKeyText(...)`で取得できる文字列が翻訳されている
-- 例: `Space`が「スペース」
- [https://docs.oracle.com/javase/jp/8/docs/api/java/awt/event/KeyEvent.html#getKeyText-int- KeyEvent#getKeyText(int) (Java Platform SE 8)]では、「これらの文字列は`awt.properties`ファイルを変更することによりローカライズが可能です。」となっているが、`%JAVA_HOME%/jre/lib/rt.jar`内に`sun/awt/resources/awt.class`などの優先順位が高いクラスがあるため、`awt_ja.properties`などを作成しても読み込まれない
-- `-Xbootclasspath/p:`などで、`rt.jar`より先に以下のような`sun.awt.resources.awt_ja.class`を読み込むよう指定
#code{{
package sun.awt.resources;
import java.util.ListResourceBundle;
// ant package
// cd target
// "%JAVA_HOME%\bin\java" -Xbootclasspath/p:example.jar -jar example.jar
// Class names should begin with an uppercase character
@SuppressWarnings({"PMD.ClassNamingConventions", "checkstyle:typename"})
public final class awt_ja extends ListResourceBundle {
  @Override protected Object[][] getContents() {
    System.out.println("awt_ja");
    return new Object[][] {
      {"AWT.space", "XXXXX"}
    };
  }
}
}}

* 参考リンク [#reference]
- [https://community.oracle.com/thread/1364746 Swing - Localized Accelorator Keys]
- [[JMenuItemの内部にJButtonを配置する>Swing/ButtonsInMenuItem]]

* コメント [#comment]
#comment
- `JMenuItem`に`GridBagLayout`を設定して右寄せする方法を追加。 -- &user(aterai); &new{2014-11-30 (日) 17:57:58};

#comment