1 package com
.intellij
.ui
;
3 import org
.jetbrains
.annotations
.NotNull
;
4 import org
.jetbrains
.annotations
.Nullable
;
7 import javax
.swing
.tree
.DefaultMutableTreeNode
;
8 import javax
.swing
.tree
.TreePath
;
10 import java
.util
.Enumeration
;
15 public class DuplicateNodeRenderer
{
16 public interface DuplicatableNode
<T
> {
17 //returns first duplicate node, if any, or null if there are none
18 //duplicate nodes are painted gray
19 @Nullable T
getDuplicate();
23 public static void paintDuplicateNodesBackground(Graphics g
, JTree tree
) {
24 Rectangle clipBounds
= g
.getClipBounds();
25 int start
= tree
.getClosestRowForLocation(clipBounds
.x
, clipBounds
.y
);
26 int end
= Math
.min(tree
.getRowCount(), tree
.getClosestRowForLocation(clipBounds
.x
+clipBounds
.width
, clipBounds
.y
+clipBounds
.height
)+1);
27 Color old
= g
.getColor();
28 for (int i
= start
; i
< end
; i
++) {
29 TreePath path
= tree
.getPathForRow(i
);
30 if (path
== null) continue;
31 DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)path
.getLastPathComponent();
32 Rectangle accumRect
= null;
33 TreePath accumPath
= null;
34 while (node
!= null) {
35 Object userObject
= node
.getUserObject();
36 if (!(userObject
instanceof DuplicatableNode
)) break;
37 DuplicatableNode duplicatableNode
= (DuplicatableNode
)userObject
;
38 Object duplicate
= duplicatableNode
.getDuplicate();
39 if (duplicate
== null) break;
40 accumPath
= accumRect
== null ? path
: accumPath
.getParentPath();
41 accumRect
= union(tree
.getPathBounds(accumPath
), accumRect
);
42 node
= (DefaultMutableTreeNode
)node
.getParent();
44 if (accumRect
!= null) {
45 Rectangle rowRect
= tree
.getRowBounds(tree
.getRowForPath(accumPath
));
46 accumRect
= accumRect
.intersection(new Rectangle(rowRect
.x
, rowRect
.y
, Integer
.MAX_VALUE
, Integer
.MAX_VALUE
));
48 //unite all expanded children node rectangles since they can stretch out of parent's
49 node
= (DefaultMutableTreeNode
)accumPath
.getLastPathComponent();
50 accumRect
= union(accumRect
, getExpandedNodesRect(tree
, node
, accumPath
));
52 g
.setColor(new Color(230, 230, 230));
53 g
.fillRoundRect(accumRect
.x
, accumRect
.y
, accumRect
.width
, accumRect
.height
, 10, 10);
54 g
.setColor(Color
.lightGray
);
55 g
.drawRoundRect(accumRect
.x
, accumRect
.y
, accumRect
.width
, accumRect
.height
, 10, 10);
62 private static Rectangle
union(Rectangle r1
, Rectangle r2
) {
63 if (r1
== null) return r2
;
64 if (r2
== null) return r1
;
68 private static Rectangle
getExpandedNodesRect(JTree tree
, DefaultMutableTreeNode node
, TreePath path
) {
69 Rectangle rect
= tree
.getRowBounds(tree
.getRowForPath(path
));
70 if (tree
.isExpanded(path
)) {
71 Enumeration
<DefaultMutableTreeNode
> children
= node
.children();
72 while (children
.hasMoreElements()) {
73 DefaultMutableTreeNode child
= children
.nextElement();
74 TreePath childPath
= path
.pathByAddingChild(child
);
75 assert !path
.equals(childPath
) : path
+";"+child
;
76 rect
= union(rect
, getExpandedNodesRect(tree
, child
, childPath
));