概要

TreeModelから親JCheckBoxと子JCheckBoxのグループを作成し、親JCheckBoxが選択されている場合のみ子JCheckBoxのグループを有効化します。

サンプルコード

class CheckBoxStatusUpdateListener implements TreeModelListener {
  private boolean adjusting;

  @Override public void treeNodesChanged(TreeModelEvent e) {
    if (adjusting) {
      return;
    }
    adjusting = true;

    DefaultTreeModel model = (DefaultTreeModel) e.getSource();
    Object[] children = e.getChildren();
    boolean isOnlyOneNodeSelected = Objects.nonNull(children) && children.length == 1;
    Object current = isOnlyOneNodeSelected ? children[0] : model.getRoot();
    if (current instanceof DefaultMutableTreeNode) {
      DefaultMutableTreeNode node = (DefaultMutableTreeNode) current;
      CheckBoxNode c = (CheckBoxNode) node.getUserObject();
      for (Object child : e.getChildren()) {
        updateAllChildrenUserObject((DefaultMutableTreeNode) child, c.selected);
      }
      model.nodeChanged((DefaultMutableTreeNode) e.getTreePath().getLastPathComponent());
    }
    adjusting = false;
  }

  private void updateAllChildrenUserObject(DefaultMutableTreeNode parent, boolean enabled) {
    // Java 9: Collections.list(parent.breadthFirstEnumeration()).stream()
    Collections.list((Enumeration<?>) parent.breadthFirstEnumeration()).stream()
        .filter(DefaultMutableTreeNode.class::isInstance)
        .map(DefaultMutableTreeNode.class::cast)
        .filter(node -> !Objects.equals(parent, node))
        .forEach(node -> {
          CheckBoxNode check = (CheckBoxNode) node.getUserObject();
          node.setUserObject(new CheckBoxNode(check.text, check.selected, enabled));
        });
  }

  @Override public void treeNodesInserted(TreeModelEvent e) {
    /* not needed */
  }

  @Override public void treeNodesRemoved(TreeModelEvent e) {
    /* not needed */
  }

  @Override public void treeStructureChanged(TreeModelEvent e) {
    /* not needed */
  }
}
View in GitHub: Java, Kotlin

解説

  • Box
    • Box.createVerticalBox()で作成した垂直BoxJCheckBoxを配置して作成
    • フォルダノードを親JCheckBox、葉ノードを子JCheckBoxのグループとして作成
    • JCheckBoxBox.createHorizontalBox()Box.createHorizontalStrut( (DefaultMutableTreeNode#getLevel() - 1) * 16))で作成したインデントと合わせて追加してから垂直Boxに配置
    • JCheckBoxActionListenerを追加し、選択状態で子JCheckBoxのグループの有効化・無効化を切り替える
      • どの親JCheckBoxの子JCheckBoxかはDefaultMutableTreeNode#children()で名前を取得して検索しているので、同名ノードが存在すると正しく動作しない
  • JTree
    • TreeCellRendererTreeCellEditorJCheckBoxを使用するよう設定
    • TreeWillExpandListenerを追加してノードの折り畳みを無効化
    • TreeModelListenerを追加してフォルダノードから作成した親JCheckBoxの選択状態が変更されたら葉ノードから作成された子JCheckBoxの有効化・無効化を切り替える
    • 孫ノードから作成されたJCheckBoxの有効化・無効化は考慮していない

参考リンク

コメント