JTextFieldやJTextAreaなどにコピーや貼り付けなどのポップアップメニューを追加する。

SwingのJTextFieldなどは、デフォルトではポップアップメニューが現れません。
ポップアップメニューは大半の場合は、コピー、貼り付けなどのメニューで
十分ですが、場合によってはそれ以外のメニューを扱いたい場合もあります。
Swingは汎用的に作られているので、設定されていないのでしょう。
でも、コピーや貼り付けぐらいは欲しいです。
JTextFieldなどのコンポーネントにはそれらのActionが最初から用意されているので、それらを使えば簡単に実装できます。
ただ、日本語ではないので、その辺の指定はしてやる必要があります。
以下のソースは JTextField, JTextArea, JPasswordFieldに対して使えます。
ポップアップメニューに表示されるメニューは、切り取り、コピー、貼り付け、す
べて選択、の4つです。
編集不可の場合(editable=false)は、切り取り、貼り付けがグレイになります。
パスワードの場合は切り取り、コピーはメニューに現れません。

package verticalviewer.util;

import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.JMenuItem;
import javax.swing.JPasswordField;
import javax.swing.JPopupMenu;
import javax.swing.text.JTextComponent;

/**
 * Swing拡張クラス.
 * 国際化対応していない。
 */
public class SwingX {

      /**
       * ポップアップメニューにメニューを追加します.
       * 別のメニューを追加したい場合はこのメソッドを使います。
       * デフォルトのメニューのみでよいのであれば、addPopupMenu(JTextComponent)が便利です。
       */
      public static void addMenuItems(JTextComponent comp, JPopupMenu popup) {
            assert comp != null;
            assert popup != null;
            ActionMap map = comp.getActionMap();
            boolean isEditable = comp.isEditable();
            if (comp instanceof JPasswordField) {
                  popup.add(createMenuItem(map.get("paste-from-clipboard"), "貼り付け(P)", KeyEvent.VK_P, isEditable));
                  popup.addSeparator();
                  popup.add(createMenuItem(map.get("select-all"), "すべて選択(A)", KeyEvent.VK_A, true));
            } else {
                  popup.add(createMenuItem(map.get("cut-to-clipboard"), "切り取り(T)", KeyEvent.VK_T, isEditable));
                  popup.add(createMenuItem(map.get("copy-to-clipboard"), "コピー(C)", KeyEvent.VK_C, true));
                  popup.add(createMenuItem(map.get("paste-from-clipboard"), "貼り付け(P)", KeyEvent.VK_P, isEditable));
                  popup.addSeparator();
                  popup.add(createMenuItem(map.get("select-all"), "すべて選択(A)", KeyEvent.VK_A, true));
            }
      }

      private static JMenuItem createMenuItem(Action action, String text, int mnemonic, boolean enable) {
            JMenuItem menu = new JMenuItem(action);
            menu.setText(text);
            menu.setMnemonic(mnemonic);
            menu.setEnabled(enable);
            return menu;
      }

      /**
       * コンポーネントに対してデフォルトのポップアップメニューを表示させるようにする.
       * マウスリスナーを追加し、その中でポップアップメニューを表示する処理を行っています。
       * デフォルト以外のメニューを追加したい場合は、addMenuItems(JTextComponent, JPopupMenu)を
       * 使ってください。
       *
       */
      public static void addPopupMenuMouseListener(JTextComponent comp) {
            assert comp != null;
            comp.addMouseListener(new TextComponentMouseListener(comp));
      }

      static class TextComponentMouseListener extends MouseAdapter {
            final JTextComponent comp;
            TextComponentMouseListener(JTextComponent comp) {
                  this.comp = comp;
            }
            public void mouseReleased(MouseEvent ev) {
                  if (ev.isPopupTrigger()) {
                        JPopupMenu popup = new JPopupMenu();
                        addMenuItems(comp, popup);
                        popup.show(comp, ev.getX(), ev.getY());
                  }
            }
      }
}

使い方はこんな感じです。

      JTextField name = new JTextField("新規作成");
      SwingX.addPopupMenuMouseListener(name);
      getContentPane().add(name);

(2006/11/21追記)あれっ?動かないかも???
(2006/11/24追記)
以前のソースだと、cut,copy,pasteが動きませんでした。select-allだけは動きました。
なぜか、cut,copy,pasteは同じ機能と思われるActionがもう一組登録されています。
名前は、"cut-to-clipboard","copy-to-clipboard","paste-from-clipboard"です。
こちらのActionを使うようにするといけました。
Swingのソースを詳しく見ていないけど、ActionEventのsourceがJTextComponentではなく、JPopupMenuになってしまうので、それで処理がうまく実行されない、っぽいです。