Swing/EditableTreePath の変更点
- 追加された行はこの色です。
- 削除された行はこの色です。
- Swing/EditableTreePath へ行く。
- Swing/EditableTreePath の差分を削除
--- 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 --- * 概要 [#summary] JTreeのノードエディタが編集を開始できるかどうかをTreePathやMouseEventから状態を取得して判断します。 `JTree`のノードエディタが編集を開始できるかどうかを`TreePath`や`MouseEvent`から状態を取得して判断します。 #download(https://drive.google.com/uc?id=1oHmohICOAH5TjHT3z4sAl7gJY0z9imbB) * サンプルコード [#sourcecode] #code(link){{ 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; } }; } }; }} * 解説 [#explanation] - `JTree#isPathEditable(TreePath)` -- [https://docs.oracle.com/javase/jp/8/docs/api/javax/swing/JTree.html#isPathEditable-javax.swing.tree.TreePath- JTree#isPathEditable(TreePath) (Java Platform SE 8)] -- デフォルトの`JTree`では引数の`TreePath`は参照せずに常に`JTree#isEditable()`を返すだけの関数のため、この関数は通常オーバーライドして使用する -- このサンプルではこの関数をオーバーライドして葉ノードの場合のみ編集可能と設定 -- 後述の`TreeCellEditor#isCellEditable(EventObject)`より先に実行される -- 引数の`TreePath`からパス内の要素数(`TreePath#getPathCount()`)や親`TreePath`などを取得して編集可・不可を判断できる -- 引数の`TreePath`から`TreePath#getLastPathComponent()`でこのパスの最後の要素(`TreeNode`、`DefaultMutableTreeNode`)を取得できるので、葉ノードかどうか(`TreeNode#isLeaf()`)やユーザーオブジェクト(`DefaultMutableTreeNode#getUserObject()`)の状態を調査して編集可・不可を判断できる - `TreeCellEditor#isCellEditable(EventObject)` -- [https://docs.oracle.com/javase/jp/8/docs/api/javax/swing/CellEditor.html#isCellEditable-java.util.EventObject- CellEditor#isCellEditable(EventObject) (Java Platform SE 8)] -- 引数の`EventObject`を使用して編集を開始できるかどうかをエディタに問い合せる -- このサンプルではこの関数をオーバーライドしてダブルクリック、またはKBD{Shift}キーもしくはKBD{Ctrl}キーがクリック時に押下されている場合編集可と設定 -- `TableCellEditor#isCellEditable(EventObject)`の場合引数の`EventObject`が`KeyEvent`になる場合があるが`DefaultTreeCellEditor#isCellEditable(EventObject)`の場合は`MouseEvent`か`null`のみになる? --- `startEditing`アクション(KBD{F2})の場合引数の`EventObject`は`null` -- 引数の`EventObject`が`MouseEvent`の場合`JTree#isPathEditable(TreePath)`の`TreePath`では取得できない以下のような情報で編集可・不可を判断できる --- `MouseEvent#getPoint()`で`JTree`座標系のクリックされた位置 --- `MouseEvent#getClickCount()`でクリック数 --- `MouseEvent#isShiftDown()`、`MouseEvent#isControlDown()`などでクリックと同時にKBD{Shift}キーなどが押されているか -- `MouseEvent#getComponent()`で親`JTree`を参照し`JTree#getPathForLocation(x, y)`で`TreePath`、`JTree#getLastSelectedPathComponent()`で`TreeNode`を取得する方法もあるが、それだけの場合は`JTree#isPathEditable(TreePath)`を使用すべき? --- たとえば[[JCheckBoxをグループでまとめて有効化する>Swing/CheckBoxGroupEnabling]]のように編集対象ノードのユーザーオブジェクトの状態で編集可・不可を判断する場合、`JTree#isPathEditable(TreePath)`よりあとに実行される`TreeCellEditor#isCellEditable(EventObject)`で調査するとユーザーオブジェクトが編集不可なのに一瞬デフォルトの?(または直前に使用された?)編集可`TreeCellEditor`が表示されてしまう * 参考リンク [#reference] - [https://docs.oracle.com/javase/jp/8/docs/api/javax/swing/JTree.html#isPathEditable-javax.swing.tree.TreePath- JTree#isPathEditable(TreePath) (Java Platform SE 8)] - [https://docs.oracle.com/javase/jp/8/docs/api/javax/swing/CellEditor.html#isCellEditable-java.util.EventObject- CellEditor#isCellEditable(EventObject) (Java Platform SE 8)] - [[JCheckBoxをグループでまとめて有効化する>Swing/CheckBoxGroupEnabling]] -- `JTree#isPathEditable(TreePath)`をオーバーライドして`TreePath`から`DefaultMutableTreeNode`、ユーザーオブジェクトを順に取得し、これが編集可状態の場合のみノードエディタを起動 - [[JTreeのノード編集をJPopupMenuからのみに制限する>Swing/StartEditingPopupMenu]] -- `TreeCellEditor#isCellEditable(EventObject)`をオーバーライドして`EventObject`が`MouseEvent`ではない場合のみノードエディタを起動 - [[JTreeのノードにクリック可能なJButtonを複数配置する>Swing/MultipleButtonsInTreeNode]] -- `TreeCellEditor#isCellEditable(EventObject)`をオーバーライドして`EventObject`が`MouseEvent`、かつクリック位置がセルレンダラー内に配置されたボタン上の場合のみノードエディタを起動 * コメント [#comment] #comment #comment