IDEA-93573 Darcula - event notifications black number
[idea/community.git] / platform / platform-impl / src / com / intellij / notification / impl / IdeNotificationArea.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.notification.impl;
17
18 import com.intellij.icons.AllIcons;
19 import com.intellij.ide.DataManager;
20 import com.intellij.ide.ui.UISettings;
21 import com.intellij.ide.ui.UISettingsListener;
22 import com.intellij.notification.EventLog;
23 import com.intellij.notification.LogModel;
24 import com.intellij.notification.Notification;
25 import com.intellij.notification.NotificationType;
26 import com.intellij.openapi.actionSystem.PlatformDataKeys;
27 import com.intellij.openapi.application.ApplicationManager;
28 import com.intellij.openapi.project.Project;
29 import com.intellij.openapi.util.Disposer;
30 import com.intellij.openapi.wm.CustomStatusBarWidget;
31 import com.intellij.openapi.wm.IconLikeCustomStatusBarWidget;
32 import com.intellij.openapi.wm.StatusBar;
33 import com.intellij.openapi.wm.ToolWindow;
34 import com.intellij.ui.ClickListener;
35 import com.intellij.ui.LayeredIcon;
36 import com.intellij.util.Alarm;
37 import com.intellij.util.ui.UIUtil;
38 import org.jetbrains.annotations.NotNull;
39 import org.jetbrains.annotations.Nullable;
40
41 import javax.swing.*;
42 import java.awt.*;
43 import java.awt.event.MouseEvent;
44 import java.util.ArrayList;
45 import java.util.List;
46
47 /**
48  * @author spleaner
49  */
50 public class IdeNotificationArea extends JLabel implements CustomStatusBarWidget, IconLikeCustomStatusBarWidget {
51   public static final String WIDGET_ID = "Notifications";
52
53   private StatusBar myStatusBar;
54   private final Alarm myLogAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
55
56   public IdeNotificationArea() {
57     Disposer.register(this, myLogAlarm);
58     UISettings.getInstance().addUISettingsListener(new UISettingsListener() {
59       @Override
60       public void uiSettingsChanged(UISettings source) {
61         updateStatus();
62       }
63     }, this);
64     new ClickListener() {
65       @Override
66       public boolean onClick(MouseEvent e, int clickCount) {
67         EventLog.toggleLog(getProject());
68         return true;
69       }
70     }.installOn(this);
71
72     ApplicationManager.getApplication().getMessageBus().connect().subscribe(LogModel.LOG_MODEL_CHANGED, new Runnable() {
73       @Override
74       public void run() {
75         ApplicationManager.getApplication().invokeLater(new Runnable() {
76           @Override
77           public void run() {
78             updateStatus();
79           }
80         });
81       }
82     });
83   }
84
85   public WidgetPresentation getPresentation(@NotNull PlatformType type) {
86     return null;
87   }
88
89   public void dispose() {
90   }
91
92   public void install(@NotNull StatusBar statusBar) {
93     myStatusBar = statusBar;
94     updateStatus();
95   }
96
97   @Nullable
98   private Project getProject() {
99     return PlatformDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext((Component) myStatusBar));
100   }
101
102   @NotNull
103   public String ID() {
104     return WIDGET_ID;
105   }
106
107   private void updateStatus() {
108     final Project project = getProject();
109     ArrayList<Notification> notifications = EventLog.getLogModel(project).getNotifications();
110     applyIconToStatusAndToolWindow(project, createIconWithNotificationCount(notifications));
111
112     int count = notifications.size();
113     setToolTipText(count > 0 ? String.format("%s notification%s pending", count, count == 1 ? "" : "s") : "No new notifications");
114
115     myStatusBar.updateWidget(ID());
116   }
117
118   private void applyIconToStatusAndToolWindow(Project project, LayeredIcon icon) {
119     if (UISettings.getInstance().HIDE_TOOL_STRIPES) {
120       setIcon(icon);
121     }
122     else {
123       ToolWindow eventLog = EventLog.getEventLog(project);
124       if (eventLog != null) {
125         eventLog.setIcon(icon);
126       }
127       setIcon(null);
128     }
129   }
130
131   private LayeredIcon createIconWithNotificationCount(ArrayList<Notification> notifications) {
132     LayeredIcon icon = new LayeredIcon(2);
133     Icon statusIcon = getPendingNotificationsIcon(AllIcons.Ide.Notifications, getMaximumType(notifications));
134     icon.setIcon(statusIcon, 0);
135     if (notifications.size() > 0) {
136       icon.setIcon(new TextIcon(this, String.valueOf(notifications.size())), 1, statusIcon.getIconWidth() - 2, 0);
137     }
138     return icon;
139   }
140
141   @Override
142   public JComponent getComponent() {
143     return this;
144   }
145
146   private static Icon getPendingNotificationsIcon(Icon defIcon, final NotificationType maximumType) {
147     if (maximumType != null) {
148       switch (maximumType) {
149         case WARNING: return AllIcons.Ide.Warning_notifications;
150         case ERROR: return AllIcons.Ide.Error_notifications;
151         case INFORMATION: return AllIcons.Ide.Info_notifications;
152       }
153     }
154     return defIcon;
155   }
156
157   @Nullable
158   private static NotificationType getMaximumType(List<Notification> notifications) {
159     NotificationType result = null;
160     for (Notification notification : notifications) {
161       if (NotificationType.ERROR == notification.getType()) {
162         return NotificationType.ERROR;
163       }
164
165       if (NotificationType.WARNING == notification.getType()) {
166         result = NotificationType.WARNING;
167       }
168       else if (result == null && NotificationType.INFORMATION == notification.getType()) {
169         result = NotificationType.INFORMATION;
170       }
171     }
172
173     return result;
174   }
175
176   private static class TextIcon implements Icon {
177     private final String myStr;
178     private final JComponent myComponent;
179     private final int myWidth;
180
181     public TextIcon(JComponent component, @NotNull String str) {
182       myStr = str;
183       myComponent = component;
184       myWidth = myComponent.getFontMetrics(calcFont()).stringWidth(myStr) + 1;
185     }
186
187     @Override
188     public boolean equals(Object o) {
189       if (this == o) return true;
190       if (!(o instanceof TextIcon)) return false;
191
192       TextIcon icon = (TextIcon)o;
193
194       if (myWidth != icon.myWidth) return false;
195       if (!myComponent.equals(icon.myComponent)) return false;
196       if (!myStr.equals(icon.myStr)) return false;
197
198       return true;
199     }
200
201     @Override
202     public int hashCode() {
203       int result = myStr.hashCode();
204       result = 31 * result + myComponent.hashCode();
205       result = 31 * result + myWidth;
206       return result;
207     }
208
209     @Override
210     public void paintIcon(Component c, Graphics g, int x, int y) {
211       Font originalFont = g.getFont();
212       Color originalColor = g.getColor();
213       g.setFont(calcFont());
214       y += getIconHeight() - g.getFontMetrics().getDescent();
215
216       g.setColor(UIUtil.getLabelForeground());
217       g.drawString(myStr, x, y);
218
219       g.setFont(originalFont);
220       g.setColor(originalColor);
221     }
222
223     private Font calcFont() {
224       return myComponent.getFont().deriveFont(Font.BOLD).deriveFont((float) getIconHeight() * 3 / 5);
225     }
226
227     @Override
228     public int getIconWidth() {
229       return myWidth;
230     }
231
232     @Override
233     public int getIconHeight() {
234       return AllIcons.Ide.Notifications.getIconHeight();
235     }
236   }
237 }