blob: 1ef2f4c7cbb7284da557d9817b239a740c0db41a [file] [log] [blame]
/*
* Copyright 2000-2014 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.notification.impl;
import com.intellij.ide.FrameStateManager;
import com.intellij.notification.*;
import com.intellij.notification.impl.ui.NotificationsUtil;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.ex.ApplicationEx;
import com.intellij.openapi.project.DumbAwareRunnable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.project.ProjectManagerAdapter;
import com.intellij.openapi.startup.StartupManager;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.popup.*;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.wm.IdeFrame;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.openapi.wm.impl.welcomeScreen.WelcomeFrame;
import com.intellij.ui.*;
import com.intellij.ui.components.panels.NonOpaquePanel;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
/**
* @author spleaner
*/
public class NotificationsManagerImpl extends NotificationsManager {
public NotificationsManagerImpl() {
ApplicationManager.getApplication().getMessageBus().connect().subscribe(Notifications.TOPIC, new MyNotificationListener(null));
}
@Override
public void expire(@NotNull final Notification notification) {
UIUtil.invokeLaterIfNeeded(new Runnable() {
@Override
public void run() {
EventLog.expireNotification(notification);
}
});
}
@Override
public <T extends Notification> T[] getNotificationsOfType(Class<T> klass, @Nullable final Project project) {
final List<T> result = new ArrayList<T>();
if (project == null || !project.isDefault() && !project.isDisposed()) {
for (Notification notification : EventLog.getLogModel(project).getNotifications()) {
if (klass.isInstance(notification)) {
//noinspection unchecked
result.add((T) notification);
}
}
}
return ArrayUtil.toObjectArray(result, klass);
}
private static void doNotify(@NotNull final Notification notification,
@Nullable NotificationDisplayType displayType,
@Nullable final Project project) {
final NotificationsConfigurationImpl configuration = NotificationsConfigurationImpl.getNotificationsConfigurationImpl();
if (!configuration.isRegistered(notification.getGroupId())) {
configuration.register(notification.getGroupId(), displayType == null ? NotificationDisplayType.BALLOON : displayType);
}
final NotificationSettings settings = NotificationsConfigurationImpl.getSettings(notification.getGroupId());
boolean shouldLog = settings.isShouldLog();
boolean displayable = settings.getDisplayType() != NotificationDisplayType.NONE;
boolean willBeShown = displayable && NotificationsConfigurationImpl.getNotificationsConfigurationImpl().SHOW_BALLOONS;
if (!shouldLog && !willBeShown) {
notification.expire();
}
if (NotificationsConfigurationImpl.getNotificationsConfigurationImpl().SHOW_BALLOONS) {
final Runnable runnable = new DumbAwareRunnable() {
@Override
public void run() {
showNotification(notification, project);
}
};
if (project == null) {
runnable.run();
} else if (!project.isDisposed()) {
StartupManager.getInstance(project).runWhenProjectIsInitialized(runnable);
}
}
}
private static void showNotification(final Notification notification, @Nullable final Project project) {
Application application = ApplicationManager.getApplication();
if (application instanceof ApplicationEx && !((ApplicationEx)application).isLoaded()) {
application.invokeLater(new Runnable() {
@Override
public void run() {
showNotification(notification, project);
}
}, ModalityState.current());
return;
}
String groupId = notification.getGroupId();
final NotificationSettings settings = NotificationsConfigurationImpl.getSettings(groupId);
NotificationDisplayType type = settings.getDisplayType();
String toolWindowId = NotificationsConfigurationImpl.getNotificationsConfigurationImpl().getToolWindowId(groupId);
if (type == NotificationDisplayType.TOOL_WINDOW &&
(toolWindowId == null || project == null || !ToolWindowManager.getInstance(project).canShowNotification(toolWindowId))) {
type = NotificationDisplayType.BALLOON;
}
switch (type) {
case NONE:
return;
//case EXTERNAL:
// notifyByExternal(notification);
// break;
case STICKY_BALLOON:
case BALLOON:
default:
Balloon balloon = notifyByBalloon(notification, type, project);
if (!settings.isShouldLog() || type == NotificationDisplayType.STICKY_BALLOON) {
if (balloon == null) {
notification.expire();
} else {
balloon.addListener(new JBPopupAdapter() {
@Override
public void onClosed(LightweightWindowEvent event) {
if (!event.isOk()) {
notification.expire();
}
}
});
}
}
break;
case TOOL_WINDOW:
MessageType messageType = notification.getType() == NotificationType.ERROR
? MessageType.ERROR
: notification.getType() == NotificationType.WARNING ? MessageType.WARNING : MessageType.INFO;
final NotificationListener notificationListener = notification.getListener();
HyperlinkListener listener = notificationListener == null ? null : new HyperlinkListener() {
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
notificationListener.hyperlinkUpdate(notification, e);
}
};
assert toolWindowId != null;
String msg = notification.getTitle();
if (StringUtil.isNotEmpty(notification.getContent())) {
if (StringUtil.isNotEmpty(msg)) {
msg += "<br>";
}
msg += notification.getContent();
}
//noinspection SSBasedInspection
ToolWindowManager.getInstance(project).notifyByBalloon(toolWindowId, messageType, msg, notification.getIcon(), listener);
}
}
@Nullable
private static Balloon notifyByBalloon(final Notification notification,
final NotificationDisplayType displayType,
@Nullable final Project project) {
if (isDummyEnvironment()) return null;
Window window = findWindowForBalloon(project);
if (window instanceof IdeFrame) {
final ProjectManager projectManager = ProjectManager.getInstance();
final boolean noProjects = projectManager.getOpenProjects().length == 0;
final boolean sticky = NotificationDisplayType.STICKY_BALLOON == displayType || noProjects;
final Balloon balloon = createBalloon((IdeFrame)window, notification, false, false);
Disposer.register(project != null ? project : ApplicationManager.getApplication(), balloon);
if (notification.isExpired()) {
return null;
}
BalloonLayout layout = ((IdeFrame)window).getBalloonLayout();
if (layout == null) return null;
layout.add(balloon);
((BalloonImpl)balloon).startFadeoutTimer(0);
if (NotificationDisplayType.BALLOON == displayType) {
FrameStateManager.getInstance().getApplicationActive().doWhenDone(new Runnable() {
@Override
public void run() {
if (balloon.isDisposed()) {
return;
}
if (!sticky) {
((BalloonImpl)balloon).startFadeoutTimer(0);
((BalloonImpl)balloon).setHideOnClickOutside(true);
}
else //noinspection ConstantConditions
if (noProjects) {
projectManager.addProjectManagerListener(new ProjectManagerAdapter() {
@Override
public void projectOpened(Project project) {
projectManager.removeProjectManagerListener(this);
if (!balloon.isDisposed()) {
((BalloonImpl)balloon).startFadeoutTimer(300);
}
}
});
}
}
});
}
return balloon;
}
return null;
}
@Nullable
public static Window findWindowForBalloon(Project project) {
Window frame = WindowManager.getInstance().getFrame(project);
if (frame == null && project == null) {
frame = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow();
}
if (frame == null && project == null) {
frame = (Window)WelcomeFrame.getInstance();
}
return frame;
}
public static Balloon createBalloon(@NotNull final IdeFrame window, final Notification notification, final boolean showCallout, final boolean hideOnClickOutside) {
final JEditorPane text = new JEditorPane();
text.setEditorKit(UIUtil.getHTMLEditorKit());
final HyperlinkListener listener = NotificationsUtil.wrapListener(notification);
if (listener != null) {
text.addHyperlinkListener(listener);
}
final JLabel label = new JLabel(NotificationsUtil.buildHtml(notification, null));
text.setText(NotificationsUtil.buildHtml(notification, "width:" + Math.min(350, label.getPreferredSize().width) + "px;"));
text.setEditable(false);
text.setOpaque(false);
if (UIUtil.isUnderNimbusLookAndFeel()) {
text.setBackground(UIUtil.TRANSPARENT_COLOR);
}
text.setBorder(null);
final JPanel content = new NonOpaquePanel(new BorderLayout((int)(label.getIconTextGap() * 1.5), (int)(label.getIconTextGap() * 1.5)));
if (text.getCaret() != null) {
text.setCaretPosition(0);
}
JScrollPane pane = ScrollPaneFactory.createScrollPane(text,
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
pane.setBorder(null);
pane.setOpaque(false);
pane.getViewport().setOpaque(false);
content.add(pane, BorderLayout.CENTER);
final NonOpaquePanel north = new NonOpaquePanel(new BorderLayout());
north.add(new JLabel(NotificationsUtil.getIcon(notification)), BorderLayout.NORTH);
content.add(north, BorderLayout.WEST);
content.setBorder(new EmptyBorder(2, 4, 2, 4));
Dimension preferredSize = text.getPreferredSize();
text.setSize(preferredSize);
Dimension paneSize = new Dimension(text.getPreferredSize());
int maxHeight = Math.min(400, window.getComponent().getHeight() - 20);
int maxWidth = Math.min(600, window.getComponent().getWidth() - 20);
if (paneSize.height > maxHeight) {
pane.setPreferredSize(new Dimension(Math.min(maxWidth, paneSize.width + UIUtil.getScrollBarWidth()), maxHeight));
} else if (paneSize.width > maxWidth) {
pane.setPreferredSize(new Dimension(maxWidth, paneSize.height + UIUtil.getScrollBarWidth()));
}
final BalloonBuilder builder = JBPopupFactory.getInstance().createBalloonBuilder(content);
builder.setFillColor(new JBColor(Gray._234, Gray._92))
.setCloseButtonEnabled(true)
.setShowCallout(showCallout)
.setShadow(false)
.setHideOnClickOutside(hideOnClickOutside)
.setHideOnAction(hideOnClickOutside)
.setHideOnKeyOutside(hideOnClickOutside)
.setHideOnFrameResize(false)
.setBorderColor(new JBColor(Gray._180, Gray._110));
final Balloon balloon = builder.createBalloon();
balloon.setAnimationEnabled(false);
notification.setBalloon(balloon);
return balloon;
}
private static boolean isDummyEnvironment() {
final Application application = ApplicationManager.getApplication();
return application.isUnitTestMode() || application.isCommandLine();
}
public static class ProjectNotificationsComponent {
public ProjectNotificationsComponent(final Project project) {
if (isDummyEnvironment()) {
return;
}
project.getMessageBus().connect().subscribe(Notifications.TOPIC, new MyNotificationListener(project));
}
}
private static class MyNotificationListener extends NotificationsAdapter {
private final Project myProject;
public MyNotificationListener(@Nullable Project project) {
myProject = project;
}
@Override
public void notify(@NotNull Notification notification) {
doNotify(notification, null, myProject);
}
}
}