Summary

JTableJTreeに設定したJPopupMenuがキー入力で開く場合、その表示位置が選択セル基準になるよう設定します。

Source Code Examples

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);
    }
  }
}
View in GitHub: Java, Kotlin

Description

  • JTable上:
    • デフォルトのJTableContextMenuキー(アプリケーションキー)やShift+F10キー入力でJPopupMenuを開くと、セル選択状態に関係なく常にJTableの中央にJPopupMenuの左上が来るよう配置される
    • キー入力によるセル編集が可能な状態では、ContextMenuキー入力でJPopupMenuのオープンとセルの編集開始が同時に発生してしまう
  • JTable下:
    • JTable#getPopupLocation(MouseEvent)をオーバーライドしてContextMenuキー入力でJPopupMenuを開く場合はリード選択セルを基準になるよう設定
      • JPopupMenuをキー入力で開く場合の表示位置を調整する
      • JTable#getCellSelectionEnabled()trueでセル選択可能の場合はリード選択セルの右下がJPopupMenuの左上となるよう配置
      • JTable#getRowSelectionAllowed()trueで行選択可能の場合はリード選択行の左下中央下がJPopupMenuの左上となるよう配置
      • リード選択セルが存在しない(JTable#getCellRect(row, col, ...)が空領域)場合はデフォルトのJTableの中央がJPopupMenuの左上となるよう配置
    • JTable#editCellAt(...)をオーバーライドしてContextMenuキーやF1F12キーなどでセルの編集を開始しないよう設定
  • JTree上:
    • デフォルトのJTreeContextMenuキー(アプリケーションキー)やShift+F10キー入力でJPopupMenuを開くと、セル選択状態に関係なく常にJTreeの中央にJPopupMenuの左上となるよう配置される
  • JTree下:
    • JTree#getPopupLocation(MouseEvent)をオーバーライドしてContextMenuキー入力でJPopupMenuを開く場合はリード選択セルを基準になるよう設定
      • リード選択セルが存在する場合はその右下がJPopupMenuの左上となるよう配置
      • リード選択セルが存在しない場合はデフォルトのJTreeの中央がJPopupMenuの左上となるよう配置

Reference

Comment