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