NPE (18686)
[fedora-idea.git] / platform / lang-impl / src / com / intellij / ui / DeferredIconImpl.java
blob9b8395040c433bcd8a5e0ccbaef2a65018362c78
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * @author max
20 package com.intellij.ui;
22 import com.intellij.concurrency.Job;
23 import com.intellij.concurrency.JobScheduler;
24 import com.intellij.openapi.progress.ProcessCanceledException;
25 import com.intellij.openapi.project.IndexNotReadyException;
26 import com.intellij.util.Alarm;
27 import com.intellij.util.Function;
28 import com.intellij.util.ui.EmptyIcon;
30 import javax.swing.*;
31 import javax.swing.plaf.TreeUI;
32 import javax.swing.plaf.basic.BasicTreeUI;
33 import java.awt.*;
34 import java.lang.ref.WeakReference;
35 import java.util.LinkedHashSet;
36 import java.util.Set;
38 public class DeferredIconImpl<T> implements DeferredIcon {
39 private static final RepaintScheduler ourRepaintScheduler = new RepaintScheduler();
40 private volatile Icon myDelegateIcon;
41 private final Function<T, Icon> myEvaluator;
42 private volatile boolean myIsScheduled = false;
43 private final T myParam;
44 private WeakReference<Component> myLastTarget = null;
45 private static final EmptyIcon EMPTY_ICON = new EmptyIcon(16, 16);
46 private boolean myNeedReadAction;
48 public DeferredIconImpl(Icon baseIcon, T param, Function<T, Icon> evaluator) {
49 this(baseIcon, param, true, evaluator);
52 public DeferredIconImpl(Icon baseIcon, T param, final boolean needReadAction, Function<T, Icon> evaluator) {
53 myParam = param;
54 myDelegateIcon = nonNull(baseIcon);
55 myEvaluator = evaluator;
56 myNeedReadAction = needReadAction;
59 private static Icon nonNull(final Icon icon) {
60 return icon != null ? icon : EMPTY_ICON;
63 public void paintIcon(final Component c, final Graphics g, final int x, final int y) {
64 myDelegateIcon.paintIcon(c, g, x, y);
66 if (!myIsScheduled) {
67 myIsScheduled = true;
69 final Component target;
71 final Container list = SwingUtilities.getAncestorOfClass(JList.class, c);
72 if (list != null) {
73 target = list;
75 else {
76 final Container tree = SwingUtilities.getAncestorOfClass(JTree.class, c);
77 if (tree != null) {
78 target = tree;
80 else {
81 final Container table = SwingUtilities.getAncestorOfClass(JTable.class, c);
82 if (table != null) {
83 target = table;
85 else {
86 target = c;
91 myLastTarget = new WeakReference<Component>(target);
93 final Job<Object> job = JobScheduler.getInstance().createJob("Evaluating deferred icon", Job.DEFAULT_PRIORITY);
94 job.addTask(new Runnable() {
95 public void run() {
96 int oldWidth = myDelegateIcon.getIconWidth();
97 myDelegateIcon = evaluate();
99 final boolean shouldRevalidate = myDelegateIcon.getIconWidth() != oldWidth;
101 //noinspection SSBasedInspection
102 SwingUtilities.invokeLater(new Runnable() {
103 public void run() {
104 if (shouldRevalidate) {
105 // revalidate will not work: jtree caches size of nodes
106 if (target instanceof JTree) {
107 final TreeUI ui = ((JTree)target).getUI();
108 if (ui instanceof BasicTreeUI) {
109 // yep, reset size cache
110 ((BasicTreeUI)ui).setLeftChildIndent(((Integer)UIManager.get("Tree.leftChildIndent")).intValue());
115 if (c == target) {
116 c.repaint(x, y, getIconWidth(), getIconHeight());
118 else {
119 ourRepaintScheduler.pushDirtyComponent(target);
126 job.schedule();
130 public Icon evaluate() {
131 final Icon[] evaluated = new Icon[1];
132 final Runnable runnable = new Runnable() {
133 public void run() {
134 try {
135 evaluated[0] = nonNull(myEvaluator.fun(myParam));
137 catch (ProcessCanceledException e) {
138 evaluated[0] = EMPTY_ICON;
140 catch (IndexNotReadyException e) {
141 evaluated[0] = EMPTY_ICON;
145 if (myNeedReadAction) {
146 IconDeferrerImpl.evaluateDeferredInReadAction(runnable);
148 else {
149 IconDeferrerImpl.evaluateDeferred(runnable);
152 checkDoesntReferenceThis(evaluated[0]);
154 return evaluated[0];
157 private void checkDoesntReferenceThis(final Icon icon) {
158 if (icon == this) {
159 throw new IllegalStateException("Loop in icons delegation");
162 if (icon instanceof DeferredIconImpl) {
163 checkDoesntReferenceThis(((DeferredIconImpl)icon).myDelegateIcon);
165 else if (icon instanceof LayeredIcon) {
166 for (Icon layer : ((LayeredIcon)icon).getAllLayers()) {
167 checkDoesntReferenceThis(layer);
170 else if (icon instanceof RowIcon) {
171 final RowIcon rowIcon = (RowIcon)icon;
172 final int count = rowIcon.getIconCount();
173 for (int i = 0; i < count; i++) {
174 checkDoesntReferenceThis(rowIcon.getIcon(i));
179 public int getIconWidth() {
180 return myDelegateIcon.getIconWidth();
183 public int getIconHeight() {
184 return myDelegateIcon.getIconHeight();
187 public void invalidate() {
188 myIsScheduled = false;
189 Component lastTarget = myLastTarget != null ? myLastTarget.get() : null;
190 if (lastTarget != null) {
191 lastTarget.repaint();
195 private static class RepaintScheduler {
196 private final Alarm myAlarm = new Alarm();
197 private final Set<Component> myQueue = new LinkedHashSet<Component>();
199 public void pushDirtyComponent(Component c) {
200 myAlarm.cancelAllRequests();
201 myAlarm.addRequest(new Runnable() {
202 public void run() {
203 for (Component component : myQueue) {
204 component.repaint();
206 myQueue.clear();
208 }, 50);
210 myQueue.add(c);