概要

JTableの行を右クリックした場合、その選択状態の変更とJPopupMenuの表示を両方実行する方法をテストします。

サンプルコード

class RightMouseButtonLayerUI extends LayerUI<JScrollPane> {
  @Override public void installUI(JComponent c) {
    super.installUI(c);
    if (c instanceof JLayer) {
      ((JLayer<?>) c).setLayerEventMask(AWTEvent.MOUSE_EVENT_MASK);
    }
  }

  @Override public void uninstallUI(JComponent c) {
    if (c instanceof JLayer) {
      ((JLayer<?>) c).setLayerEventMask(0);
    }
    super.uninstallUI(c);
  }

  @Override protected void processMouseEvent(
        MouseEvent e, JLayer<? extends JScrollPane> l) {
    Component c = e.getComponent();
    if (c instanceof JTable && SwingUtilities.isRightMouseButton(e)) {
      JTable table = (JTable) c;
      if (table.isEditing()) {
        table.removeEditor();
      }
      Point pt = e.getPoint();
      Rectangle r = TableUtils.getCellArea(table);
      if (r.contains(pt)) {
        int currentRow = table.rowAtPoint(pt);
        int currentColumn = table.columnAtPoint(pt);
        if (TableUtils.isNotRowContains(table.getSelectedRows(), currentRow)) {
          table.changeSelection(currentRow, currentColumn, false, false);
        }
      } else {
        table.clearSelection();
      }
    } else {
      super.processMouseEvent(e, l);
    }
  }
}

final class TableUtils {
  private TableUtils() {
    /* Singleton */
  }

  public static Rectangle getCellArea(JTable table) {
    Rectangle start = table.getCellRect(0, 0, true);
    int rc = table.getRowCount();
    int cc = table.getColumnCount();
    Rectangle end = table.getCellRect(rc - 1, cc - 1, true);
    return start.union(end);
  }

  public static boolean isNotRowContains(int[] selectedRows, int currentRow) {
    for (int i : selectedRows) {
      if (i == currentRow) {
        return false;
      }
    }
    return true;
  }
}
View in GitHub: Java, Kotlin

解説

  • Default
    • デフォルトのマウスクリックによるJTableの行選択では!SwingUtilities.isLeftMouseButton(MouseEvent)で左クリック以外は無視される
    • このため右クリックでは行の選択状態は変更されない
  • MouseListener
    • JTableMouseListenerを追加してmousePressed(...)をオーバーライドし、JPopupMenuを開く動作はそのまま残して別途右クリックでの選択状態変更動作を定義
      • JTableが編集状態の場合はJTable#removeEditor()を実行してそれをキャンセル
      • JTableのセル領域以外で右クリックされた場合はJTable#clearSelection()を実行して選択状態をクリアし、JPopupMenuを開く
      • JTableの行選択領域内で右クリックされた場合は選択状態を維持したままJPopupMenuを開く
      • JTableのセル領域かつ行選択領域外で右クリックされた場合は右クリックされた行のみを選択状に変更し、JPopupMenuを開く
    • 制限: JPopupMenuが開いた状態のまま連続して右クリックするとMouseListener#mousePressed(...)が実行されない(Windows 11環境では問題ない?)ため、JPopupMenuは右クリックした位置に再オープンされるが行選択状態は変更できない
  • PopupMenuListener
    • JPopupMenuPopupMenuListenerを追加してpopupMenuWillBecomeVisible(PopupMenuEvent)をオーバーライドし、JPopupMenuを開く動作はそのまま残して別途右クリックでの選択状態変更動作を定義
      • 右クリックで実行する動作の定義はMouseListenerと同様
    • 制限: JPopupMenuの左上隅を右クリックされた位置として使用するため、画面端などでJPopupMenuの表示位置が調整される場合は右クリックされた行が選択されない場合がある
  • JLayer
    • JTableの親JScrollPaneJLayerでラップしてprocessMouseEvent(...)をオーバーライドし、JPopupMenuを開く動作はそのまま残して別途右クリックでの選択状態変更動作を定義
      • 右クリックで実行する動作の定義はMouseListenerと同様

参考リンク

コメント