---
category: swing
folder: PopupLocationRelativeToCell
title: JPopupMenuがキー入力で開く位置を選択セル基準に変更する
tags: [JPopupMenu, JTable, JTree]
author: aterai
pubdate: 2025-06-23T02:20:54+09:00
description: JTableやJTreeに設定したJPopupMenuがキー入力で開く場合、その表示位置が選択セル基準になるよう設定します。
image: https://drive.google.com/uc?id=1ygP91Udeh85p7GTZ7PWNL3brntM4FSPf
---
* Summary [#summary]
`JTable`や`JTree`に設定した`JPopupMenu`がキー入力で開く場合、その表示位置が選択セル基準になるよう設定します。

#download(https://drive.google.com/uc?id=)

* Source Code Examples [#sourcecode]
#code(link){{
class PopupLocationTable extends JTable {
  private static final List<Integer> IGNORE_KEYS = Arrays.asList(
      KeyEvent.VK_F1, KeyEvent.VK_F2, KeyEvent.VK_F3,
      KeyEvent.VK_F4, KeyEvent.VK_F5, KeyEvent.VK_F6,
      KeyEvent.VK_F7, KeyEvent.VK_F8, KeyEvent.VK_F9,
      KeyEvent.VK_F10, KeyEvent.VK_F11, KeyEvent.VK_F12,
      KeyEvent.VK_F13, KeyEvent.VK_F14, KeyEvent.VK_F15,
      KeyEvent.VK_F16, KeyEvent.VK_F17, KeyEvent.VK_F18,
      KeyEvent.VK_F19, KeyEvent.VK_F20, KeyEvent.VK_F21,
      KeyEvent.VK_F22, KeyEvent.VK_F23, KeyEvent.VK_CONTEXT_MENU);

  protected PopupLocationTable(int numRows, int numColumns) {
    super(numRows, numColumns);
  }

  @Override public Point getPopupLocation(MouseEvent e) {
    Rectangle r = getLeadSelectionCellRect();
    boolean b = e == null && !r.isEmpty();
    return b ? getKeyPopupLocation(r) : super.getPopupLocation(e);
  }

  @Override public boolean editCellAt(int row, int column, EventObject e) {
    return !isIgnoreKeys(e) && super.editCellAt(row, column, e);
  }

  private Point getKeyPopupLocation(Rectangle r) {
    double px = getCellSelectionEnabled()
        ? r.getMaxX()
        : getBounds().getCenterX();
    return new Point((int) px, (int) r.getMaxY());
  }

  private Rectangle getLeadSelectionCellRect() {
    int row = getSelectionModel().getLeadSelectionIndex();
    int col = getColumnModel().getSelectionModel().getLeadSelectionIndex();
    return getCellRect(row, col, false);
  }

  private static boolean isIgnoreKeys(EventObject e) {
    return e instanceof KeyEvent &&
           IGNORE_KEYS.contains(((KeyEvent) e).getKeyCode());
  }
}

final class TablePopupMenu extends JPopupMenu {
  /* default */ TablePopupMenu() {
    super();
    JMenuItem check = new JCheckBoxMenuItem("setCellSelectionEnabled", true);
    add(check).addActionListener(e -> {
      boolean b = ((JCheckBoxMenuItem) e.getSource()).isSelected();
      JTable table = (JTable) getInvoker();
      table.setCellSelectionEnabled(b);
      table.setRowSelectionAllowed(true);
    });
    add("clearSelectionAndLeadAnchor").addActionListener(e -> {
      JTable table = (JTable) getInvoker();
      clearSelectionAndLeadAnchor(table);
    });
  }

  private static void clearSelectionAndLeadAnchor(JTable table) {
    ListSelectionModel selectionModel = table.getSelectionModel();
    ListSelectionModel colSelectionModel = table.getColumnModel().getSelectionModel();
    selectionModel.setValueIsAdjusting(true);
    colSelectionModel.setValueIsAdjusting(true);
    table.clearSelection();
    selectionModel.setAnchorSelectionIndex(-1);
    selectionModel.setLeadSelectionIndex(-1);
    colSelectionModel.setAnchorSelectionIndex(-1);
    colSelectionModel.setLeadSelectionIndex(-1);
    selectionModel.setValueIsAdjusting(false);
    colSelectionModel.setValueIsAdjusting(false);
  }

  @Override public void show(Component c, int x, int y) {
    if (c instanceof JTable) {
      super.show(c, x, y);
    }
  }
}
}}

* Description [#description]
- `JTable`上:
-- デフォルトの`JTable`でKBD{ContextMenu}キー(アプリケーションキー)やKBD{Shift+F10}キー入力で`JPopupMenu`を開くと、セル選択状態に関係なく常に`JTable`の中央に`JPopupMenu`の左上となるよう配置される
-- デフォルトの`JTable`でKBD{ContextMenu}キー(アプリケーションキー)やKBD{Shift+F10}キー入力で`JPopupMenu`を開くと、セル選択状態に関係なく常に`JTable`の中央に`JPopupMenu`の左上が来るよう配置される
-- キー入力によるセル編集が可能な状態では、KBD{ContextMenu}キー入力で`JPopupMenu`のオープンとセルの編集開始が同時に発生してしまう
- `JTable`下:
-- `JTable#getPopupLocation(MouseEvent)`をオーバーライドしてKBD{ContextMenu}キー入力で`JPopupMenu`を開く場合はリード選択セルを基準になるよう設定
--- [[JPopupMenuをキー入力で開く場合の表示位置を調整する>Swing/PopupLocation]]
--- `JTable#getCellSelectionEnabled()`が`true`でセル選択可能の場合はリード選択セルの右下が`JPopupMenu`の左上となるよう配置
--- `JTable#getRowSelectionAllowed()`が`true`で行選択可能の場合はリード選択行の%%左下%%中央下が`JPopupMenu`の左上となるよう配置
--- リード選択セルが存在しない(`JTable#getCellRect(row, col, ...)`が空)場合はデフォルトの`JTable`の中央が`JPopupMenu`の左上となるよう配置
--- リード選択セルが存在しない(`JTable#getCellRect(row, col, ...)`が空領域)場合はデフォルトの`JTable`の中央が`JPopupMenu`の左上となるよう配置
-- `JTable#editCellAt(...)`をオーバーライドしてKBD{ContextMenu}キーやKBD{F1}~KBD{F12}キーなどでセルの編集を開始しないよう設定
--- [[JTableでキー入力によるセル編集自動開始を一部禁止する>Swing/FunctionKeyStartEditing]]
- `JTree`上:
-- デフォルトの`JTree`でKBD{ContextMenu}キー(アプリケーションキー)やKBD{Shift+F10}キー入力で`JPopupMenu`を開くと、セル選択状態に関係なく常に`JTree`の中央に`JPopupMenu`の左上となるよう配置される
- `JTree`下:
-- `JTree#getPopupLocation(MouseEvent)`をオーバーライドしてKBD{ContextMenu}キー入力で`JPopupMenu`を開く場合はリード選択セルを基準になるよう設定
--- リード選択セルが存在する場合はその右下が`JPopupMenu`の左上となるよう配置
--- リード選択セルが存在しない場合はデフォルトの`JTree`の中央が`JPopupMenu`の左上となるよう配置

* Reference [#reference]
- [[JPopupMenuをキー入力で開く場合の表示位置を調整する>Swing/PopupLocation]]
- [[JTableでキー入力によるセル編集自動開始を一部禁止する>Swing/FunctionKeyStartEditing]]

* Comment [#comment]
#comment
#comment