void (Graphics g, JTree tree) { Rectangle clipBounds = g.getClipBounds(); int start = tree.getClosestRowForLocation(clipBounds.x, clipBounds.y); int end = Math.min(tree.getRowCount(), tree.getClosestRowForLocation(clipBounds.x+clipBounds.width, clipBounds.y+clipBounds.height)+1); Color old = g.getColor(); for (int i = start; i < end; i++) { TreePath path = tree.getPathForRow(i); if (path == null) continue; DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent(); Rectangle accumRect = null; TreePath accumPath = null; while (node != null) { Object userObject = node.getUserObject(); if (!(userObject instanceof DuplicatableNode duplicatableNode)) break; Object duplicate = duplicatableNode.getDuplicate(); if (duplicate == null) break; accumPath = accumRect == null ? path : accumPath.getParentPath(); accumRect = union(tree.getPathBounds(accumPath), accumRect); node = (DefaultMutableTreeNode)node.getParent(); } if (accumRect != null) { Rectangle rowRect = tree.getRowBounds(tree.getRowForPath(accumPath)); accumRect = accumRect.intersection(new Rectangle(rowRect.x, rowRect.y, Integer.MAX_VALUE, Integer.MAX_VALUE)); //unite all expanded children node rectangles since they can stretch out of parent's node = (DefaultMutableTreeNode)accumPath.getLastPathComponent(); accumRect = union(accumRect, getExpandedNodesRect(tree, node, accumPath)); g.setColor(Gray._230); g.fillRoundRect(accumRect.x, accumRect.y, accumRect.width, accumRect.height, 10, 10); g.setColor(Color.lightGray); g.drawRoundRect(accumRect.x, accumRect.y, accumRect.width, accumRect.height, 10, 10); } } g.setColor(old); }