diff --git a/platform/platform-impl/src/com/intellij/ui/AppIcon.java b/platform/platform-impl/src/com/intellij/ui/AppIcon.java index ad00afa33539..8103572dfdc9 100644 --- a/platform/platform-impl/src/com/intellij/ui/AppIcon.java +++ b/platform/platform-impl/src/com/intellij/ui/AppIcon.java @@ -17,9 +17,11 @@ package com.intellij.ui; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.wm.IdeFrame; import java.awt.*; import java.awt.image.BufferedImage; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public abstract class AppIcon { @@ -29,9 +31,13 @@ public abstract class AppIcon { static AppIcon ourMacImpl; static AppIcon ourEmptyImpl; - public abstract void setProgress(double value, boolean isOk); + public abstract void setProgress(IdeFrame frame, Object processId, AppIconScheme.Progress scheme, double value, boolean isOk); - public abstract void hideProgress(); + public abstract void hideProgress(IdeFrame frame, Object processId); + + public abstract void setBadge(String text); + + public abstract void requestAttention(boolean critical); public static AppIcon getInstance() { if (ourMacImpl == null) { @@ -47,86 +53,118 @@ public abstract class AppIcon { } } + private static class MacAppIcon extends AppIcon { private static BufferedImage myAppImage; - private static BufferedImage myCurrentImage; + private Object myCurrentProcessId; - private boolean myProgressShown; + private double myUpdateFraction = 0.05d; + private double myLastValue; - private MacAppIcon() { + private BufferedImage getAppImage() { try { - Object app = getApp().invoke(null); + if (myAppImage != null) return myAppImage; + + Object app = getApp(); Image appImage = (Image)getAppMethod("getDockIconImage").invoke(app); + if (appImage == null) return null; + int width = appImage.getWidth(null); int height = appImage.getHeight(null); BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); Graphics2D g2d = img.createGraphics(); g2d.drawImage(appImage, null, null); myAppImage = img; + + } + catch (Exception e) { + LOG.error(e); + } + + return myAppImage; + } + + @Override + public void setBadge(String text) { + try { + getAppMethod("setDockIconBadge", String.class).invoke(getApp(), text); } catch (Exception e) { LOG.error(e); } } - private Method getAppMethod(final String name, Class... args) throws NoSuchMethodException, ClassNotFoundException { - return getAppClass().getMethod(name, args); - } - - private Method getApp() throws NoSuchMethodException, ClassNotFoundException { - return getAppClass().getMethod("getApplication"); - } - - private Class getAppClass() throws ClassNotFoundException { - return Class.forName("com.apple.eawt.Application"); + @Override + public void requestAttention(boolean critical) { + try { + getAppMethod("requestUserAttention", Boolean.class).invoke(getApp(), Boolean.valueOf(critical)); + } + catch (NoSuchMethodException e) { + return; + } catch (Exception e) { + LOG.error(e); + } } @Override - public void hideProgress() { + public void hideProgress(IdeFrame frame, Object processId) { + if (getAppImage() == null) return; + try { - if (myProgressShown) { - setDockIcon(myAppImage); + if (myCurrentProcessId != null && myCurrentProcessId.equals(processId)) { + setDockIcon(getAppImage()); } } finally { - myProgressShown = false; + myCurrentProcessId = null; + myLastValue = 0; } } - public void setProgress(double progress, boolean isOk) { + public void setProgress(IdeFrame frame, Object processId, AppIconScheme.Progress scheme, double value, boolean isOk) { + if (getAppImage() == null) return; + + if (myCurrentProcessId != null && !myCurrentProcessId.equals(processId)) return; + + myCurrentProcessId = processId; + + if (Math.abs(myLastValue - value) < myUpdateFraction) return; + try { - int progressHeight = 45; + int progressHeight = 65; int xInset = 30; - int yInset = 10; + int yInset = 25; int arc = 25; Rectangle progressRec = new Rectangle(new Point(xInset, myAppImage.getHeight() - yInset - progressHeight), new Dimension(myAppImage.getWidth() - xInset * 2, progressHeight)); - myCurrentImage = new BufferedImage(myAppImage.getWidth(), myAppImage.getHeight(), BufferedImage.TYPE_INT_ARGB); - Graphics2D g = myCurrentImage.createGraphics(); + BufferedImage current = new BufferedImage(myAppImage.getWidth(), myAppImage.getHeight(), BufferedImage.TYPE_INT_ARGB); + Graphics2D g = current.createGraphics(); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - g.drawImage(myAppImage, null, null); + g.drawImage(getAppImage(), null, null); g.setColor(Color.black); g.fillRoundRect(progressRec.x, progressRec.y, progressRec.width, progressRec.height, arc, arc); - g.setColor(isOk ? Color.green : Color.red); + g.setColor(isOk ? scheme.getOkColor() : scheme.getErrorColor()); - int currentWidth = (int)Math.ceil(progressRec.width * progress); + int currentWidth = (int)Math.ceil(progressRec.width * value); g.fillRoundRect(progressRec.x, progressRec.y, currentWidth, progressRec.height, arc, arc); - setDockIcon(myCurrentImage); + setDockIcon(current); + + myLastValue = value; } catch (Exception e) { LOG.error(e); } finally { - myProgressShown = true; + myCurrentProcessId = null; } } @@ -138,15 +176,36 @@ public abstract class AppIcon { LOG.error(e); } } + + private Method getAppMethod(final String name, Class... args) throws NoSuchMethodException, ClassNotFoundException { + return getAppClass().getMethod(name, args); + } + + private Object getApp() throws NoSuchMethodException, ClassNotFoundException, InvocationTargetException, IllegalAccessException { + return getAppClass().getMethod("getApplication").invoke(null); + } + + private Class getAppClass() throws ClassNotFoundException { + return Class.forName("com.apple.eawt.Application"); + } + } private static class EmptyIcon extends AppIcon { @Override - public void setProgress(double value, boolean isOk) { + public void setProgress(IdeFrame frame, Object processId, AppIconScheme.Progress scheme, double value, boolean isOk) { } @Override - public void hideProgress() { + public void hideProgress(IdeFrame frame, Object processId) { + } + + @Override + public void setBadge(String text) { + } + + @Override + public void requestAttention(boolean critical) { } } diff --git a/platform/platform-impl/src/com/intellij/ui/AppIconScheme.java b/platform/platform-impl/src/com/intellij/ui/AppIconScheme.java new file mode 100644 index 000000000000..75e342a8a9a4 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ui/AppIconScheme.java @@ -0,0 +1,49 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.ui; + +import java.awt.*; + +public class AppIconScheme { + + public interface Progress { + + static final Progress TESTS = new Progress() { + public Color getOkColor() { + return Color.green; + } + + public Color getErrorColor() { + return Color.red; + } + }; + + static final Progress BUILD = new Progress() { + public Color getOkColor() { + return Color.blue; + } + + public Color getErrorColor() { + return Color.red; + } + }; + + Color getOkColor(); + Color getErrorColor(); + + } + +} diff --git a/plugins/junit/src/com/intellij/execution/junit2/ui/TestProgress.java b/plugins/junit/src/com/intellij/execution/junit2/ui/TestProgress.java index 24c17c9ca84e..601cffecb9fe 100644 --- a/plugins/junit/src/com/intellij/execution/junit2/ui/TestProgress.java +++ b/plugins/junit/src/com/intellij/execution/junit2/ui/TestProgress.java @@ -24,15 +24,19 @@ import com.intellij.execution.junit2.ui.model.JUnitAdapter; import com.intellij.execution.junit2.ui.model.JUnitRunningModel; import com.intellij.execution.testframework.Filter; import com.intellij.execution.testframework.AbstractTestProxy; +import com.intellij.openapi.Disposable; import com.intellij.openapi.diagnostic.Logger; import com.intellij.rt.execution.junit.states.PoolOfTestStates; import javax.swing.*; +import com.intellij.ui.AppIcon; +import com.intellij.ui.AppIconScheme; import org.jetbrains.annotations.NonNls; -public class TestProgress extends DefaultBoundedRangeModel { +public class TestProgress extends DefaultBoundedRangeModel implements Disposable { private static final Logger LOG = Logger.getInstance("#com.intellij.execution.junit2.ui.TestProgress"); + private static final String TESTS = "tests"; private int myProblemsCounter = 0; private TestProxy myCurrentState = null; private final MyJUnitListener myListener = new MyJUnitListener(); @@ -120,4 +124,24 @@ public class TestProgress extends DefaultBoundedRangeModel { myMissingChildren--; } } + + @Override + public void setValue(int n) { + super.setValue(n); + AppIcon icon = AppIcon.getInstance(); + if (n < getMaximum()) { + icon.setProgress(null, TESTS, AppIconScheme.Progress.TESTS, (double)n / (double)getMaximum(), myProblemsCounter == 0); + } else { + icon.hideProgress(null, TESTS); + if (myProblemsCounter > 0) { + icon.setBadge(String.valueOf(myProblemsCounter)); + icon.requestAttention(false); + } + } + } + + public void dispose() { + AppIcon.getInstance().hideProgress(null, TESTS); + AppIcon.getInstance().setBadge(null); + } } diff --git a/plugins/junit/src/com/intellij/execution/junit2/ui/model/JUnitRunningModel.java b/plugins/junit/src/com/intellij/execution/junit2/ui/model/JUnitRunningModel.java index 4e1a44dc215e..dc02fb25e567 100644 --- a/plugins/junit/src/com/intellij/execution/junit2/ui/model/JUnitRunningModel.java +++ b/plugins/junit/src/com/intellij/execution/junit2/ui/model/JUnitRunningModel.java @@ -57,6 +57,7 @@ public class JUnitRunningModel implements TestFrameworkRunningModel { myProperties = properties; myRoot.setEventsConsumer(myNotifier); myProgress = new TestProgress(this); + Disposer.register(this, myProgress); Disposer.register(this, myTreeListener); Disposer.register(this, new Disposable() { public void dispose() {