• category: swing folder: EditableTreePath title: JTreeのTreeNodeが編集を開始可能かを調査する tags: [JTree, TreeCellEditor, TreePath, TreeNode, DefaultMutableTreeNode] author: aterai pubdate: 2024-02-26T02:07:52+09:00 description: JTreeのノードエディタが編集を開始できるかどうかをTreePathやMouseEventから状態を取得して判断します。 image: https://drive.google.com/uc?id=1oHmohICOAH5TjHT3z4sAl7gJY0z9imbB

概要

JTreeのノードエディタが編集を開始できるかどうかをTreePathやMouseEventから状態を取得して判断します。

サンプルコード

JTree tree = new JTree() {
  @Override public void updateUI() {
    super.updateUI();
    setEditable(true);
    setCellEditor(makeTreeCellEditor(this));
  }

  @Override public boolean isPathEditable(TreePath path) {
    appendLog("JTree#isPathEditable(TreePath)");
    return Optional.ofNullable(path.getLastPathComponent())
        .filter(TreeNode.class::isInstance)
        .map(node -> {
          boolean isLeaf = ((TreeNode) node).isLeaf();
          appendLog(String.format("  isLeaf: %s", isLeaf));
          if (node instanceof DefaultMutableTreeNode) {
            int lv = ((DefaultMutableTreeNode) node).getLevel();
            appendLog(String.format("  getLevel: %d", lv));
          }
          return isLeaf;
        })
        .orElse(false);
  }

  private TreeCellEditor makeTreeCellEditor(JTree tree) {
    return new DefaultTreeCellEditor(
        tree, (DefaultTreeCellRenderer) tree.getCellRenderer()) {
      @Override public boolean isCellEditable(EventObject e) {
        appendLog("TreeCellEditor#isCellEditable(EventObject)");
        boolean ret;
        if (e instanceof MouseEvent) {
          MouseEvent me = (MouseEvent) e;
          appendLog("  MouseEvent");
          appendLog(String.format("  getPoint(): %s", me.getPoint()));
          appendLog(String.format("  getClickCount: %d", me.getClickCount()));
          appendLog(String.format("  isShiftDown: %s", me.isShiftDown()));
          appendLog(String.format("  isControlDown: %s", me.isControlDown()));
          ret = me.getClickCount() >= 2 || me.isShiftDown() || me.isControlDown();
        } else if (e instanceof KeyEvent) {
          appendLog("  KeyEvent");
          ret = super.isCellEditable(e);
        } else { // e == null
          appendLog("  startEditing Action(F2)");
          ret = super.isCellEditable(e);
        }
        return ret;
      }
    };
  }
};
View in GitHub: Java, Kotlin

解説

  • JTree#isPathEditable(TreePath)
    • JTree#isPathEditable(TreePath) (Java Platform SE 8)
    • デフォルトのJTreeでは引数のTreePathは参照せずに常にJTree#isEditable()を返すだけの関数のため、この関数は通常オーバーライドして使用する
    • このサンプルではこの関数をオーバーライドして葉ノードの場合のみ編集可能と設定
    • 後述のTreeCellEditor#isCellEditable(EventObject)より先に実行される
    • 引数のTreePathからパス内の要素数(TreePath#getPathCount())や親TreePathなどを取得して編集可・不可を判断できる
    • 引数のTreePathからTreePath#getLastPathComponent()でこのパスの最後の要素(TreeNodeDefaultMutableTreeNode)を取得できるので、葉ノードかどうか(TreeNode#isLeaf())やユーザーオブジェクト(DefaultMutableTreeNode#getUserObject())の状態を調査して編集可・不可を判断できる
  • TreeCellEditor#isCellEditable(EventObject)
    • CellEditor#isCellEditable(EventObject) (Java Platform SE 8)
    • 引数のEventObjectを使用して編集を開始できるかどうかをエディタに問い合せる
    • このサンプルではこの関数をオーバーライドしてダブルクリック、またはShiftキーもしくはCtrlキーがクリック時に押下されている場合編集可と設定
    • TableCellEditor#isCellEditable(EventObject)の場合引数のEventObjectKeyEventになる場合があるがDefaultTreeCellEditor#isCellEditable(EventObject)の場合はMouseEventnullのみになる?
      • startEditingアクション(F2)の場合引数のEventObjectnull
    • 引数のEventObjectMouseEventの場合JTree#isPathEditable(TreePath)TreePathでは取得できない以下のような情報で編集可・不可を判断できる
      • MouseEvent#getPoint()JTree座標系のクリックされた位置
      • MouseEvent#getClickCount()でクリック数
      • MouseEvent#isShiftDown()MouseEvent#isControlDown()などでクリックと同時にShiftキーなどが押されているか
    • MouseEvent#getComponent()で親JTreeを参照しJTree#getPathForLocation(x, y)TreePathJTree#getLastSelectedPathComponent()TreeNodeを取得する方法もあるが、それだけの場合はJTree#isPathEditable(TreePath)を使用すべき?
      • たとえばJCheckBoxをグループでまとめて有効化するのように編集対象ノードのユーザーオブジェクトの状態で編集可・不可を判断する場合、JTree#isPathEditable(TreePath)よりあとに実行されるTreeCellEditor#isCellEditable(EventObject)で調査するとユーザーオブジェクトが編集不可なのに一瞬デフォルトの?(または直前に使用された?)編集可TreeCellEditorが表示されてしまう

参考リンク

コメント