[ex] 17058 ava.lang.AssertionError: Target component is not showing
[fedora-idea.git] / platform / platform-impl / src / com / intellij / openapi / wm / impl / status / InfoAndProgressPanel.java
blobd7d5be357de2526dcef197a4af75c1511c4f53d9
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.
16 package com.intellij.openapi.wm.impl.status;
18 import com.intellij.idea.ActionsBundle;
19 import com.intellij.openapi.application.ApplicationManager;
20 import com.intellij.openapi.editor.Editor;
21 import com.intellij.openapi.progress.TaskInfo;
22 import com.intellij.openapi.ui.MessageType;
23 import com.intellij.openapi.ui.popup.Balloon;
24 import com.intellij.openapi.ui.popup.BalloonHandler;
25 import com.intellij.openapi.ui.popup.JBPopupFactory;
26 import com.intellij.openapi.util.MultiValuesMap;
27 import com.intellij.openapi.wm.StatusBar;
28 import com.intellij.openapi.wm.ex.ProgressIndicatorEx;
29 import com.intellij.ui.awt.RelativePoint;
30 import com.intellij.ui.components.labels.LinkLabel;
31 import com.intellij.ui.components.labels.LinkListener;
32 import com.intellij.ui.components.panels.Wrapper;
33 import com.intellij.util.Alarm;
34 import com.intellij.util.ui.AbstractLayoutManager;
35 import com.intellij.util.ui.AsyncProcessIcon;
36 import com.intellij.util.ui.update.MergingUpdateQueue;
37 import com.intellij.util.ui.update.Update;
38 import org.jetbrains.annotations.NotNull;
40 import javax.swing.*;
41 import javax.swing.border.Border;
42 import javax.swing.border.CompoundBorder;
43 import javax.swing.border.EmptyBorder;
44 import javax.swing.event.HyperlinkListener;
45 import java.awt.*;
46 import java.awt.event.MouseAdapter;
47 import java.awt.event.MouseEvent;
48 import java.util.ArrayList;
49 import java.util.Collection;
50 import java.util.HashMap;
51 import java.util.Map;
53 public class InfoAndProgressPanel extends JPanel implements StatusBarPatch {
54 private final ProcessPopup myPopup;
55 private final TextPanel myInfoPanel = new TextPanel(true);
57 private final ArrayList<ProgressIndicatorEx> myOriginals = new ArrayList<ProgressIndicatorEx>();
58 private final ArrayList<TaskInfo> myInfos = new ArrayList<TaskInfo>();
59 private final Map<InlineProgressIndicator, ProgressIndicatorEx> myInline2Original
60 = new HashMap<InlineProgressIndicator, ProgressIndicatorEx>();
61 private final MultiValuesMap<ProgressIndicatorEx, InlineProgressIndicator> myOriginal2Inlines
62 = new MultiValuesMap<ProgressIndicatorEx, InlineProgressIndicator>();
64 private final MergingUpdateQueue myUpdateQueue;
65 private final AsyncProcessIcon myProgressIcon;
66 private final Alarm myQueryAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
68 private boolean myShouldClosePopupAndOnProcessFinish;
69 private final CompoundBorder myCompoundBorder;
71 public InfoAndProgressPanel(final StatusBar statusBar) {
72 setOpaque(false);
73 final Border emptyBorder = BorderFactory.createEmptyBorder(0, 2, 0, 2);
74 myInfoPanel.setBorder(emptyBorder);
75 myInfoPanel.setOpaque(false);
77 myCompoundBorder = BorderFactory.createCompoundBorder(new StatusBarImpl.SeparatorBorder.Left(), new EmptyBorder(0, 2, 0, 2));
79 myProgressIcon = new AsyncProcessIcon("Background process");
80 myProgressIcon.setOpaque(true);
82 myProgressIcon.addMouseListener(new MouseAdapter() {
83 @Override
84 public void mousePressed(MouseEvent e) {
85 if (!myPopup.isShowing()) {
86 openProcessPopup();
89 });
91 myProgressIcon.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
93 StatusBarTooltipper.install(this, myProgressIcon, statusBar);
95 myUpdateQueue = new MergingUpdateQueue("Progress indicator", 50, true, MergingUpdateQueue.ANY_COMPONENT);
96 myPopup = new ProcessPopup(this);
98 restoreEmptyStatus();
101 public JComponent getComponent() {
102 return this;
105 public String updateStatusBar(final Editor selected, final JComponent componentSelected) {
106 return ActionsBundle.message("action.ShowProcessWindow.double.click");
109 public void clear() {
113 public void addProgress(final ProgressIndicatorEx original, TaskInfo info) {
114 synchronized (myOriginals) {
115 final boolean veryFirst = myOriginals.isEmpty();
117 myOriginals.add(original);
118 myInfos.add(info);
120 final InlineProgressIndicator expanded = createInlineDelegate(info, original, false);
121 final InlineProgressIndicator compact = createInlineDelegate(info, original, true);
123 myPopup.addIndicator(expanded);
124 myProgressIcon.resume();
126 if (veryFirst && !myPopup.isShowing()) {
127 buildInInlineIndicator(compact);
129 else {
130 buildInProcessCount();
133 runQuery();
137 private void removeProgress(InlineProgressIndicator progress) {
138 synchronized (myOriginals) {
139 if (!myInline2Original.containsKey(progress)) return;
141 final boolean last = myOriginals.size() == 1;
142 final boolean beforeLast = myOriginals.size() == 2;
144 myPopup.removeIndicator(progress);
146 final ProgressIndicatorEx original = removeFromMaps(progress);
147 if (myOriginals.contains(original)) return;
149 if (last) {
150 restoreEmptyStatus();
151 if (myShouldClosePopupAndOnProcessFinish) {
152 hideProcessPopup();
155 else {
156 if (myPopup.isShowing() || myOriginals.size() > 1) {
157 buildInProcessCount();
159 else if (beforeLast) {
160 buildInInlineIndicator(createInlineDelegate(myInfos.get(0), myOriginals.get(0), true));
162 else {
163 restoreEmptyStatus();
167 runQuery();
171 private ProgressIndicatorEx removeFromMaps(final InlineProgressIndicator progress) {
172 final ProgressIndicatorEx original = myInline2Original.get(progress);
174 myInline2Original.remove(progress);
176 myOriginal2Inlines.remove(original, progress);
177 if (myOriginal2Inlines.get(original) == null) {
178 final int originalIndex = myOriginals.indexOf(original);
179 myOriginals.remove(originalIndex);
180 myInfos.remove(originalIndex);
183 return original;
186 private void openProcessPopup() {
187 synchronized (myOriginals) {
188 if (myPopup.isShowing()) return;
189 if (!myOriginals.isEmpty()) {
190 myShouldClosePopupAndOnProcessFinish = true;
191 buildInProcessCount();
193 else {
194 myShouldClosePopupAndOnProcessFinish = false;
195 restoreEmptyStatus();
197 myPopup.show();
201 void hideProcessPopup() {
202 synchronized (myOriginals) {
203 if (!myPopup.isShowing()) return;
205 if (myOriginals.size() == 1) {
206 buildInInlineIndicator(createInlineDelegate(myInfos.get(0), myOriginals.get(0), true));
208 else if (myOriginals.isEmpty()) {
209 restoreEmptyStatus();
211 else {
212 buildInProcessCount();
215 myPopup.hide();
219 private void buildInProcessCount() {
220 removeAll();
221 setLayout(new BorderLayout());
223 final JPanel progressCountPanel = new JPanel(new BorderLayout(0, 2));
224 String processWord = myOriginals.size() == 1 ? " process" : " processes";
225 final LinkLabel label = new LinkLabel(myOriginals.size() + processWord + " running...", null, new LinkListener() {
226 public void linkSelected(final LinkLabel aSource, final Object aLinkData) {
227 triggerPopupShowing();
230 label.setOpaque(true);
232 final Wrapper labelComp = new Wrapper(label);
233 progressCountPanel.add(labelComp, BorderLayout.CENTER);
235 myProgressIcon.setBorder(myCompoundBorder);
236 progressCountPanel.add(myProgressIcon, BorderLayout.WEST);
238 add(myInfoPanel, BorderLayout.CENTER);
240 progressCountPanel.setBorder(new EmptyBorder(0, 0, 0, 4));
241 add(progressCountPanel, BorderLayout.EAST);
243 revalidate();
244 repaint();
247 private void buildInInlineIndicator(final InlineProgressIndicator inline) {
248 removeAll();
249 setLayout(new InlineLayout());
250 add(myInfoPanel);
252 final JPanel inlinePanel = new JPanel(new BorderLayout());
254 inline.getComponent().setBorder(new EmptyBorder(0, 0, 0, 2));
255 inlinePanel.add(inline.getComponent(), BorderLayout.CENTER);
257 myProgressIcon.setBorder(myCompoundBorder);
258 inlinePanel.add(myProgressIcon, BorderLayout.WEST);
260 inline.updateProgressNow();
262 add(inlinePanel);
264 myInfoPanel.revalidate();
265 myInfoPanel.repaint();
268 public void setText(final String text) {
269 myInfoPanel.setText(text);
272 public BalloonHandler notifyByBalloon(MessageType type, String htmlBody, Icon icon, HyperlinkListener listener) {
273 final Balloon balloon = JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(
274 htmlBody.replace("\n", "<br>"),
275 icon != null ? icon : type.getDefaultIcon(),
276 type.getPopupBackground(),
277 listener).createBalloon();
279 SwingUtilities.invokeLater(new Runnable() {
280 public void run() {
281 Component comp = InfoAndProgressPanel.this;
282 if (comp.isShowing()) {
283 int offset = comp.getHeight() / 2;
284 Point point = new Point(comp.getWidth() - offset, comp.getHeight() - offset);
285 balloon.show(new RelativePoint(comp, point), Balloon.Position.above);
290 return new BalloonHandler() {
291 public void hide() {
292 SwingUtilities.invokeLater(new Runnable() {
293 public void run() {
294 balloon.hide();
301 private static class InlineLayout extends AbstractLayoutManager {
303 public Dimension preferredLayoutSize(final Container parent) {
304 Dimension result = new Dimension();
305 for (int i = 0; i < parent.getComponentCount(); i++) {
306 final Dimension prefSize = parent.getComponent(i).getPreferredSize();
307 result.width += prefSize.width;
308 result.height = Math.max(prefSize.height, result.height);
310 return result;
313 public void layoutContainer(final Container parent) {
314 final Dimension size = parent.getSize();
315 int compWidth = size.width / parent.getComponentCount();
316 int eachX = 0;
317 for (int i = 0; i < parent.getComponentCount(); i++) {
318 final Component each = parent.getComponent(i);
319 if (i == parent.getComponentCount() - 1) {
320 compWidth = size.width - eachX;
322 each.setBounds(eachX, 0, compWidth, size.height);
323 eachX += compWidth;
328 private InlineProgressIndicator createInlineDelegate(final TaskInfo info, final ProgressIndicatorEx original, final boolean compact) {
329 final Collection<InlineProgressIndicator> inlines = myOriginal2Inlines.get(original);
330 if (inlines != null) {
331 for (InlineProgressIndicator eachInline : inlines) {
332 if (eachInline.isCompact() == compact) return eachInline;
336 final InlineProgressIndicator inline = new MyInlineProgressIndicator(compact, info, original);
338 myInline2Original.put(inline, original);
339 myOriginal2Inlines.put(original, inline);
341 if (compact) {
342 inline.getComponent().addMouseListener(new MouseAdapter() {
343 @Override
344 public void mousePressed(MouseEvent e) {
345 if (!myPopup.isShowing()) {
346 openProcessPopup();
352 return inline;
355 private void triggerPopupShowing() {
356 if (myPopup.isShowing()) {
357 hideProcessPopup();
359 else {
360 openProcessPopup();
364 private void restoreEmptyStatus() {
365 removeAll();
366 setLayout(new BorderLayout());
367 add(myInfoPanel, BorderLayout.CENTER);
368 myProgressIcon.setBorder(myCompoundBorder);
369 add(myProgressIcon, BorderLayout.EAST);
370 myProgressIcon.suspend();
371 myInfoPanel.revalidate();
372 myInfoPanel.repaint();
375 public boolean isProcessWindowOpen() {
376 return myPopup.isShowing();
379 public void setProcessWindowOpen(final boolean open) {
380 if (open) {
381 openProcessPopup();
383 else {
384 hideProcessPopup();
388 private class MyInlineProgressIndicator extends InlineProgressIndicator {
389 private final ProgressIndicatorEx myOriginal;
390 private final TaskInfo myTask;
392 public MyInlineProgressIndicator(final boolean compact, final TaskInfo task, final ProgressIndicatorEx original) {
393 super(compact, task);
394 myOriginal = original;
395 myTask = task;
396 original.addStateDelegate(this);
399 public void cancel() {
400 super.cancel();
401 updateProgress();
404 @Override
405 public void stop() {
406 super.stop();
407 updateProgress();
410 @Override
411 protected boolean isFinished() {
412 return isFinished(myTask);
415 @Override
416 public void finish(@NotNull final TaskInfo task) {
417 super.finish(task);
418 queueRunningUpdate(new Runnable() {
419 public void run() {
420 removeProgress(MyInlineProgressIndicator.this);
421 dispose();
426 protected void cancelRequest() {
427 myOriginal.cancel();
430 protected void queueProgressUpdate(final Runnable update) {
431 myUpdateQueue.queue(new Update(MyInlineProgressIndicator.this, false, 1) {
432 public void run() {
433 ApplicationManager.getApplication().invokeLater(update);
438 protected void queueRunningUpdate(final Runnable update) {
439 myUpdateQueue.queue(new Update(new Object(), false, 0) {
440 public void run() {
441 ApplicationManager.getApplication().invokeLater(update);
447 private void runQuery() {
448 if (getRootPane() == null) return;
450 synchronized (myOriginals) {
451 for (InlineProgressIndicator each : myInline2Original.keySet()) {
452 each.updateProgress();
455 myQueryAlarm.addRequest(new Runnable() {
456 public void run() {
457 runQuery();
459 }, 2000);