2 * Copyright 2000-2011 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.notification.impl;
18 import com.intellij.ide.FrameStateManager;
19 import com.intellij.notification.*;
20 import com.intellij.notification.impl.ui.NotificationsUtil;
21 import com.intellij.openapi.application.ApplicationManager;
22 import com.intellij.openapi.components.ApplicationComponent;
23 import com.intellij.openapi.project.Project;
24 import com.intellij.openapi.project.ProjectManager;
25 import com.intellij.openapi.project.ProjectManagerAdapter;
26 import com.intellij.openapi.startup.StartupManager;
27 import com.intellij.openapi.ui.MessageType;
28 import com.intellij.openapi.ui.popup.*;
29 import com.intellij.openapi.util.Disposer;
30 import com.intellij.openapi.util.text.StringUtil;
31 import com.intellij.openapi.wm.ToolWindowManager;
32 import com.intellij.openapi.wm.WindowManager;
33 import com.intellij.openapi.wm.impl.IdeFrameImpl;
34 import com.intellij.ui.BalloonImpl;
35 import com.intellij.ui.components.panels.NonOpaquePanel;
36 import com.intellij.util.ArrayUtil;
37 import com.intellij.util.PairFunction;
38 import com.intellij.util.ui.UIUtil;
39 import org.jetbrains.annotations.NotNull;
40 import org.jetbrains.annotations.Nullable;
43 import javax.swing.border.EmptyBorder;
44 import javax.swing.event.HyperlinkEvent;
45 import javax.swing.event.HyperlinkListener;
47 import java.util.ArrayList;
48 import java.util.Arrays;
49 import java.util.Collection;
50 import java.util.List;
55 public class NotificationsManagerImpl extends NotificationsManager implements Notifications, ApplicationComponent {
57 private final NotificationModel myModel = new NotificationModel();
60 public String getComponentName() {
61 return "NotificationsManager";
64 public void initComponent() {
65 ApplicationManager.getApplication().getMessageBus().connect().subscribe(TOPIC, this);
68 public static NotificationsManagerImpl getNotificationsManagerImpl() {
69 return (NotificationsManagerImpl) ApplicationManager.getApplication().getComponent(NotificationsManager.class);
72 public void notify(@NotNull Notification notification) {
73 doNotify(notification, NotificationDisplayType.BALLOON);
77 public void register(@NotNull String groupDisplayName, @NotNull NotificationDisplayType defaultDisplayType) {
81 public void register(@NotNull String groupDisplayName,
82 @NotNull NotificationDisplayType defaultDisplayType,
87 public void expire(@NotNull Notification notification) {
88 EventLog.expire(notification);
93 public <T extends Notification> T[] getNotificationsOfType(Class<T> klass, @Nullable final Project project) {
94 final List<Notification> notifications = myModel.getByType(null, createFilter(project, false));
95 final List<T> result = new ArrayList<T>();
96 for (final Notification notification : notifications) {
97 if (klass.isInstance(notification)) {
98 //noinspection unchecked
99 result.add((T) notification);
103 return ArrayUtil.toObjectArray(result, klass);
106 private static final PairFunction<Notification, Project, Boolean> ALL = new PairFunction<Notification, Project, Boolean>() {
108 public Boolean fun(final Notification notification, final Project project) {
113 private static final PairFunction<Notification, Project, Boolean> APPLICATION = new PairFunction<Notification, Project, Boolean>() {
115 public Boolean fun(final Notification notification, final Project project) {
116 return project == null;
120 public void clear(@Nullable Project project) {
121 myModel.clear(createFilter(project, true));
124 public void disposeComponent() {
128 protected void doNotify(@NotNull Notification notification, @Nullable final NotificationDisplayType displayType) {
129 doNotify(notification, displayType, null);
132 public void doNotify(@NotNull final Notification notification, @Nullable NotificationDisplayType displayType, @Nullable final Project project) {
133 final NotificationsConfigurationImpl configuration = NotificationsConfigurationImpl.getNotificationsConfigurationImpl();
134 if (!configuration.isRegistered(notification.getGroupId())) {
135 configuration.register(notification.getGroupId(), displayType == null ? NotificationDisplayType.BALLOON : displayType);
138 final NotificationSettings settings = NotificationsConfigurationImpl.getSettings(notification.getGroupId());
139 boolean shouldLog = settings.isShouldLog();
140 boolean displayable = settings.getDisplayType() != NotificationDisplayType.NONE;
141 if (shouldLog && displayable) {
142 myModel.add(notification, project);
145 boolean willBeShown = displayable && NotificationsConfigurationImpl.getNotificationsConfigurationImpl().SHOW_BALLOONS;
146 if (!shouldLog && !willBeShown) {
147 notification.expire();
150 if (NotificationsConfigurationImpl.getNotificationsConfigurationImpl().SHOW_BALLOONS) {
151 final Runnable runnable = new Runnable() {
154 showNotification(notification, project);
157 if (project == null) {
159 } else if (!project.isDisposed()) {
160 StartupManager.getInstance(project).runWhenProjectIsInitialized(runnable);
165 private static void showNotification(final Notification notification, @Nullable final Project project) {
166 String groupId = notification.getGroupId();
167 final NotificationSettings settings = NotificationsConfigurationImpl.getSettings(groupId);
169 NotificationDisplayType type = settings.getDisplayType();
170 String toolWindowId = NotificationsConfigurationImpl.getNotificationsConfigurationImpl().getToolWindowId(groupId);
171 if (type == NotificationDisplayType.TOOL_WINDOW &&
172 (toolWindowId == null || project == null || !Arrays.asList(ToolWindowManager.getInstance(project).getToolWindowIds()).contains(toolWindowId))) {
173 type = NotificationDisplayType.BALLOON;
180 // notifyByExternal(notification);
185 Balloon balloon = notifyByBalloon(notification, type, project);
186 if (!settings.isShouldLog()) {
187 if (balloon == null) {
188 notification.expire();
190 balloon.addListener(new JBPopupAdapter() {
192 public void onClosed(LightweightWindowEvent event) {
193 notification.expire();
200 MessageType messageType = notification.getType() == NotificationType.ERROR
202 : notification.getType() == NotificationType.WARNING ? MessageType.WARNING : MessageType.INFO;
203 final NotificationListener notificationListener = notification.getListener();
204 HyperlinkListener listener = notificationListener == null ? null : new HyperlinkListener() {
206 public void hyperlinkUpdate(HyperlinkEvent e) {
207 notificationListener.hyperlinkUpdate(notification, e);
210 assert toolWindowId != null;
211 String msg = notification.getTitle();
212 if (StringUtil.isNotEmpty(notification.getContent())) {
213 if (StringUtil.isNotEmpty(msg)) {
216 msg += notification.getContent();
219 //noinspection SSBasedInspection
220 ToolWindowManager.getInstance(project).notifyByBalloon(toolWindowId, messageType, msg, notification.getIcon(), listener);
225 private static Balloon notifyByBalloon(final Notification notification,
226 final NotificationDisplayType displayType,
227 @Nullable final Project project) {
228 if (ApplicationManager.getApplication().isUnitTestMode()) return null;
230 Window window = findWindowForBalloon(project);
231 if (window instanceof IdeFrameImpl) {
232 final ProjectManager projectManager = ProjectManager.getInstance();
233 final boolean noProjects = projectManager.getOpenProjects().length == 0;
234 final boolean sticky = NotificationDisplayType.STICKY_BALLOON == displayType || noProjects;
235 final Balloon balloon = createBalloon(notification, false, false);
236 Disposer.register(project != null ? project : ApplicationManager.getApplication(), balloon);
237 ((IdeFrameImpl)window).getBalloonLayout().add(balloon);
238 if (NotificationDisplayType.BALLOON == displayType) {
239 FrameStateManager.getInstance().getApplicationActive().doWhenDone(new Runnable() {
242 if (balloon.isDisposed()) {
247 ((BalloonImpl)balloon).startFadeoutTimer(3000);
249 else //noinspection ConstantConditions
251 projectManager.addProjectManagerListener(new ProjectManagerAdapter() {
253 public void projectOpened(Project project) {
254 projectManager.removeProjectManagerListener(this);
255 if (!balloon.isDisposed()) {
256 ((BalloonImpl)balloon).startFadeoutTimer(300);
270 public static Window findWindowForBalloon(Project project) {
271 final JFrame frame = WindowManager.getInstance().getFrame(project);
272 if (frame == null && project == null) {
273 return KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow();
278 public static Balloon createBalloon(final Notification notification, final boolean showCallout, final boolean hideOnClickOutside) {
279 final JEditorPane text = new JEditorPane();
280 text.setEditorKit(UIUtil.getHTMLEditorKit());
282 final HyperlinkListener listener = NotificationsUtil.wrapListener(notification);
283 if (listener != null) {
284 text.addHyperlinkListener(listener);
287 final JLabel label = new JLabel(NotificationsUtil.buildHtml(notification, null));
288 text.setText(NotificationsUtil.buildHtml(notification, "width:" + Math.min(400, label.getPreferredSize().width) + "px;"));
289 text.setEditable(false);
290 text.setOpaque(false);
292 if (UIUtil.isUnderNimbusLookAndFeel()) {
293 text.setBackground(UIUtil.TRANSPARENT_COLOR);
296 text.setBorder(null);
298 final JPanel content = new NonOpaquePanel(new BorderLayout((int)(label.getIconTextGap() * 1.5), (int)(label.getIconTextGap() * 1.5)));
300 final NonOpaquePanel textWrapper = new NonOpaquePanel(new GridBagLayout());
301 textWrapper.add(text);
302 content.add(textWrapper, BorderLayout.CENTER);
304 final NonOpaquePanel north = new NonOpaquePanel(new BorderLayout());
305 north.add(new JLabel(NotificationsUtil.getIcon(notification)), BorderLayout.NORTH);
306 content.add(north, BorderLayout.WEST);
308 content.setBorder(new EmptyBorder(2, 4, 2, 4));
310 final BalloonBuilder builder = JBPopupFactory.getInstance().createBalloonBuilder(content);
311 builder.setFillColor(NotificationsUtil.getBackground(notification)).setCloseButtonEnabled(true).setShowCallout(showCallout)
312 .setHideOnClickOutside(hideOnClickOutside)
313 .setHideOnAction(hideOnClickOutside)
314 .setHideOnKeyOutside(hideOnClickOutside).setHideOnFrameResize(false);
316 final Balloon balloon = builder.createBalloon();
317 notification.setBalloon(balloon);
321 private static PairFunction<Notification, Project, Boolean> createFilter(@Nullable final Project project, final boolean strict) {
322 return project == null ? APPLICATION : new ProjectFilter(project, strict);
326 public Notification remove(Notification notification) {
327 return myModel.remove(notification);
330 public Collection<Notification> getByType(@Nullable final NotificationType type, @Nullable final Project project) {
331 return myModel.getByType(type, createFilter(project, false));
334 private static class ProjectFilter implements PairFunction<Notification, Project, Boolean> {
335 private final Project myProject;
336 private final boolean myStrict;
338 private ProjectFilter(@NotNull final Project project, final boolean strict) {
344 public Boolean fun(final Notification notification, final Project project) {
345 return myStrict ? project == myProject : project == null || project == myProject;