package org.limewire.ui.swing.tray;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.StringTokenizer;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JLabel;
import javax.swing.JRadioButton;
import javax.swing.JWindow;
import net.miginfocom.swing.MigLayout;
import org.jdesktop.application.Resource;
import org.jdesktop.swingx.JXPanel;
import org.limewire.listener.EventListener;
import org.limewire.listener.EventListenerList;
import org.limewire.listener.ListenerSupport;
import org.limewire.ui.swing.animate.AnimatorEvent;
import org.limewire.ui.swing.animate.FadeInOutAnimator;
import org.limewire.ui.swing.animate.MoveAnimator;
import org.limewire.ui.swing.util.FontUtils;
import org.limewire.ui.swing.util.GuiUtils;
import org.limewire.util.StringUtils;
import org.limewire.util.SystemUtils;
/**
* Notification window for system messages. This class handles drawing the
* window and kickstarts the animation.
*/
class NotificationWindow extends JWindow implements ListenerSupport<WindowDisposedEvent> {
private final Notification notification;
private final EventListenerList<WindowDisposedEvent> eventListenerList;
@Resource
private Icon trayNotifyClose;
@Resource
private Icon trayNotifyCloseRollover;
@Resource
private Font titleFont;
@Resource
private Color titleFontColor;
@Resource
private Font bodyFont;
@Resource
private Color bodyFontColor;
@Resource
private Font linkFont;
@Resource
private Color linkFontColor;
@Resource
private Color backgroundColor;
@Resource
private Color borderColour;
private MoveAnimator currentMoveAnimator;
public NotificationWindow(Icon icon, final Notification notification) {
eventListenerList = new EventListenerList<WindowDisposedEvent>();
GuiUtils.assignResources(this);
this.notification = notification;
setAlwaysOnTop(true);
SystemUtils.setWindowTopMost(this);
FadeInOutAnimator fadeInOutAnimator = new FadeInOutAnimator(this, 500, 2500, 500);
fadeInOutAnimator.addListener(new EventListener<AnimatorEvent<JWindow>>() {
@Override
public void handleEvent(AnimatorEvent event) {
if (event.getType() == AnimatorEvent.Type.STOPPED) {
eventListenerList.broadcast(new WindowDisposedEvent(NotificationWindow.this));
}
}
});
JXPanel panel = new JXPanel(new MigLayout("fill, gap 0px 0px, insets 0 5 5 0"));
add(panel);
panel.setBackground(backgroundColor);
panel.setBorder(BorderFactory.createLineBorder(borderColour, 2));
PerformNotificationActionsMouseListener performNotificationActionsMouseListener = new PerformNotificationActionsMouseListener();
panel.addMouseListener(performNotificationActionsMouseListener);
final JRadioButton closeButton = new JRadioButton(trayNotifyClose);
closeButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
eventListenerList.broadcast(new WindowDisposedEvent(NotificationWindow.this));
}
});
closeButton.addMouseListener(new HoverButtonMouseListener(closeButton, trayNotifyClose,
trayNotifyCloseRollover));
String titleLine1 = " ";// keeping at least a space so icon in JLabel
// renders properly
String titleLine2 = "";
if (notification.getTitle() != null) {
StringTokenizer title = new StringTokenizer(notification.getTitle(), " \t\n\r");
StringBuffer titleBuffer1 = new StringBuffer();
StringBuffer remainingMessage = buildLine(titleBuffer1, title, titleFont, 150);
titleLine1 = titleBuffer1.toString().trim();
titleLine2 = FontUtils.getTruncatedMessage(remainingMessage.toString().trim(), titleFont, 180);
}
JLabel titleLabel1 = new JLabel(titleLine1);
titleLabel1.setIcon(icon);
titleLabel1.setIconTextGap(3);
titleLabel1.setFont(titleFont);
titleLabel1.setForeground(titleFontColor);
JLabel titleLabel2 = new JLabel(titleLine2);
titleLabel2.setFont(titleFont);
titleLabel2.setForeground(titleFontColor);
titleLabel1.addMouseListener(performNotificationActionsMouseListener);
titleLabel2.addMouseMotionListener(performNotificationActionsMouseListener);
StringTokenizer message = new StringTokenizer(notification.getMessage(), " \t\n\r");
StringBuffer messageBuffer1 = new StringBuffer();
StringBuffer remainingMessage = buildLine(messageBuffer1, message, bodyFont, 180);
String messageLine1 = messageBuffer1.toString().trim();
String messageLine2 = FontUtils.getTruncatedMessage(remainingMessage.toString().trim(), bodyFont, 180);
JLabel messageLabel1 = new JLabel(messageLine1);
messageLabel1.setFont(bodyFont);
messageLabel1.setForeground(bodyFontColor);
JLabel messageLabel2 = new JLabel(messageLine2);
messageLabel2.setFont(bodyFont);
messageLabel2.setForeground(bodyFontColor);
messageLabel1.addMouseListener(performNotificationActionsMouseListener);
messageLabel2.addMouseListener(performNotificationActionsMouseListener);
// adding components to panel
panel.add(titleLabel1, "aligny top, gaptop 5");
panel.add(closeButton, "alignx right, aligny top, wrap");
if (!StringUtils.isEmpty(titleLine2)) {
panel.add(titleLabel2, "spanx 2, wrap");
}
panel.add(messageLabel1, "spanx 2, wrap, gaptop 6");
if (!StringUtils.isEmpty(messageLine2)) {
panel.add(messageLabel2, "spanx 2, wrap");
}
// if actions are available add a launchable link
if (notification.getActions() != null && notification.getActions().length > 0) {
String launchLinkHtml = "<html><u>" + notification.getActionName() + "</u></html>";
JLabel launchLink = new JLabel(launchLinkHtml);
launchLink.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
launchLink.setFont(linkFont);
launchLink.setForeground(linkFontColor);
launchLink.addMouseListener(performNotificationActionsMouseListener);
panel.add(launchLink, "spanx 2, alignx right, aligny bottom, gaptop 6, gapright 5");
}
setPreferredSize(new Dimension(204, 97));
pack();
fadeInOutAnimator.start();
}
private class PerformNotificationActionsMouseListener extends MouseAdapter {
@Override
public void mouseClicked(MouseEvent e) {
performActions();
}
}
private void performActions() {
if (notification.getActions() != null) {
for (Action action : notification.getActions()) {
action.actionPerformed(new ActionEvent(NotificationWindow.this,
ActionEvent.ACTION_PERFORMED, "Message Clicked"));
}
}
}
/**
* Returns the notification represented by this window.
*/
public Notification getNotification() {
return notification;
}
@Override
public void addNotify() {
super.addNotify();
SystemUtils.setWindowTopMost(this);
}
@Override
public void addListener(EventListener<WindowDisposedEvent> listener) {
eventListenerList.addListener(listener);
}
@Override
public boolean removeListener(EventListener<WindowDisposedEvent> listener) {
return eventListenerList.removeListener(listener);
}
/**
* Adds a line of text to line StringBuffer passed in the argument. All
* remaining text is returned in a new StringBuffer.
*/
private StringBuffer buildLine(StringBuffer line, StringTokenizer message, Font font,
int pixelWidth) {
StringBuffer remaining = new StringBuffer();
// find the first line.
while (message.hasMoreTokens()) {
String currentToken = message.nextToken();
int pixels = FontUtils.getPixelWidth(line + currentToken, font);
if (pixels < (pixelWidth)) {
line.append(currentToken);
if (message.hasMoreTokens()) {
line.append(" ");
}
} else {
remaining.append(currentToken);
if (message.hasMoreTokens()) {
remaining.append(" ");
}
break;
}
}
while (message.hasMoreTokens()) {
String currentToken = message.nextToken();
remaining.append(currentToken);
if (message.hasMoreTokens()) {
remaining.append(" ");
}
}
return remaining;
}
/**
* Sets the rollover image for the close button when moused over.
*/
private final static class HoverButtonMouseListener extends MouseAdapter {
private final JRadioButton closeButton;
private final Icon trayNotifyClose;
private final Icon trayNotifyCloseRollover;
private HoverButtonMouseListener(JRadioButton closeButton, Icon trayNotifyClose,
Icon trayNotifyCloseRollover) {
this.closeButton = closeButton;
this.trayNotifyClose = trayNotifyClose;
this.trayNotifyCloseRollover = trayNotifyCloseRollover;
}
@Override
public void mouseEntered(MouseEvent e) {
closeButton.setIcon(trayNotifyCloseRollover);
}
@Override
public void mouseExited(MouseEvent e) {
closeButton.setIcon(trayNotifyClose);
}
}
/**
* Moves the window from its current location to the new one.
*/
public synchronized void moveTo(Point newLocation) {
if (this.currentMoveAnimator != null) {
currentMoveAnimator.stop();
}
MoveAnimator moveAnimator = new MoveAnimator(this, 250, newLocation);
moveAnimator.start();
currentMoveAnimator = moveAnimator;
}
}