DeferredIcon fix: now should relayout the tree if evaluated icon's width differs...
[fedora-idea.git] / lang-impl / src / com / intellij / ui / DeferredIconImpl.java
blob569532ebd8a89710e2220696c9d6269feda376e0
1 /*
2 * @author max
3 */
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;
13 import javax.swing.*;
14 import javax.swing.plaf.TreeUI;
15 import javax.swing.plaf.basic.BasicTreeUI;
16 import java.awt.*;
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) {
33 myParam = param;
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);
46 if (!myIsScheduled) {
47 myIsScheduled = true;
49 final Component target;
51 final Container list = SwingUtilities.getAncestorOfClass(JList.class, c);
52 if (list != null) {
53 target = list;
55 else {
56 final Container tree = SwingUtilities.getAncestorOfClass(JTree.class, c);
57 if (tree != null) {
58 target = tree;
60 else {
61 final Container table = SwingUtilities.getAncestorOfClass(JTable.class, c);
62 if (table != null) {
63 target = table;
65 else {
66 target = 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() {
75 public void run() {
76 int oldWidth = myDelegateIcon.getIconWidth();
77 myDelegateIcon = evaluate();
79 final boolean shouldRevalidate = myDelegateIcon.getIconWidth() != oldWidth;
81 //noinspection SSBasedInspection
82 SwingUtilities.invokeLater(new Runnable() {
83 public void run() {
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());
95 if (c == target) {
96 c.repaint(x, y, getIconWidth(), getIconHeight());
98 else {
99 target.repaint();
106 job.schedule();
110 public Icon evaluate() {
111 final Icon[] evaluated = new Icon[1];
112 final Runnable runnable = new Runnable() {
113 public void run() {
114 try {
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);
128 else {
129 IconDeferrerImpl.evaluateDeferred(runnable);
132 checkDoesntReferenceThis(evaluated[0]);
134 return evaluated[0];
137 private void checkDoesntReferenceThis(final Icon icon) {
138 if (icon == this) {
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();