4 package com
.intellij
.ui
;
6 import com
.intellij
.concurrency
.Job
;
7 import com
.intellij
.concurrency
.JobScheduler
;
8 import com
.intellij
.openapi
.progress
.ProcessCanceledException
;
9 import com
.intellij
.openapi
.project
.IndexNotReadyException
;
10 import com
.intellij
.util
.Function
;
11 import com
.intellij
.util
.ui
.EmptyIcon
;
14 import javax
.swing
.plaf
.TreeUI
;
15 import javax
.swing
.plaf
.basic
.BasicTreeUI
;
17 import java
.lang
.ref
.WeakReference
;
19 public class DeferredIconImpl
<T
> implements DeferredIcon
{
20 private volatile Icon myDelegateIcon
;
21 private final Function
<T
, Icon
> myEvaluator
;
22 private volatile boolean myIsScheduled
= false;
23 private final T myParam
;
24 private WeakReference
<Component
> myLastTarget
= null;
25 private static final EmptyIcon EMPTY_ICON
= new EmptyIcon(16, 16);
26 private boolean myNeedReadAction
;
28 public DeferredIconImpl(Icon baseIcon
, T param
, Function
<T
, Icon
> evaluator
) {
29 this(baseIcon
, param
, true, evaluator
);
32 public DeferredIconImpl(Icon baseIcon
, T param
, final boolean needReadAction
, Function
<T
, Icon
> evaluator
) {
34 myDelegateIcon
= nonNull(baseIcon
);
35 myEvaluator
= evaluator
;
36 myNeedReadAction
= needReadAction
;
39 private static Icon
nonNull(final Icon icon
) {
40 return icon
!= null ? icon
: EMPTY_ICON
;
43 public void paintIcon(final Component c
, final Graphics g
, final int x
, final int y
) {
44 myDelegateIcon
.paintIcon(c
, g
, x
, y
);
49 final Component target
;
51 final Container list
= SwingUtilities
.getAncestorOfClass(JList
.class, c
);
56 final Container tree
= SwingUtilities
.getAncestorOfClass(JTree
.class, c
);
61 final Container table
= SwingUtilities
.getAncestorOfClass(JTable
.class, c
);
71 myLastTarget
= new WeakReference
<Component
>(target
);
73 final Job
<Object
> job
= JobScheduler
.getInstance().createJob("Evaluating deferred icon", Job
.DEFAULT_PRIORITY
);
74 job
.addTask(new Runnable() {
76 int oldWidth
= myDelegateIcon
.getIconWidth();
77 myDelegateIcon
= evaluate();
79 final boolean shouldRevalidate
= myDelegateIcon
.getIconWidth() != oldWidth
;
81 //noinspection SSBasedInspection
82 SwingUtilities
.invokeLater(new Runnable() {
84 if (shouldRevalidate
) {
85 // revalidate will not work: jtree caches size of nodes
86 if (target
instanceof JTree
) {
87 final TreeUI ui
= ((JTree
)target
).getUI();
88 if (ui
instanceof BasicTreeUI
) {
89 // yep, reset size cache
90 ((BasicTreeUI
)ui
).setLeftChildIndent(((Integer
)UIManager
.get("Tree.leftChildIndent")).intValue());
96 c
.repaint(x
, y
, getIconWidth(), getIconHeight());
110 public Icon
evaluate() {
111 final Icon
[] evaluated
= new Icon
[1];
112 final Runnable runnable
= new Runnable() {
115 evaluated
[0] = nonNull(myEvaluator
.fun(myParam
));
117 catch (ProcessCanceledException e
) {
118 evaluated
[0] = EMPTY_ICON
;
120 catch (IndexNotReadyException e
) {
121 evaluated
[0] = EMPTY_ICON
;
125 if (myNeedReadAction
) {
126 IconDeferrerImpl
.evaluateDeferredInReadAction(runnable
);
129 IconDeferrerImpl
.evaluateDeferred(runnable
);
132 checkDoesntReferenceThis(evaluated
[0]);
137 private void checkDoesntReferenceThis(final Icon icon
) {
139 throw new IllegalStateException("Loop in icons delegation");
142 if (icon
instanceof DeferredIconImpl
) {
143 checkDoesntReferenceThis(((DeferredIconImpl
)icon
).myDelegateIcon
);
145 else if (icon
instanceof LayeredIcon
) {
146 for (Icon layer
: ((LayeredIcon
)icon
).getAllLayers()) {
147 checkDoesntReferenceThis(layer
);
150 else if (icon
instanceof RowIcon
) {
151 final RowIcon rowIcon
= (RowIcon
)icon
;
152 final int count
= rowIcon
.getIconCount();
153 for (int i
= 0; i
< count
; i
++) {
154 checkDoesntReferenceThis(rowIcon
.getIcon(i
));
159 public int getIconWidth() {
160 return myDelegateIcon
.getIconWidth();
163 public int getIconHeight() {
164 return myDelegateIcon
.getIconHeight();
167 public void invalidate() {
168 myIsScheduled
= false;
169 Component lastTarget
= myLastTarget
!= null ? myLastTarget
.get() : null;
170 if (lastTarget
!= null) {
171 lastTarget
.repaint();