/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
*
* 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 net.java.sip.communicator.impl.swingnotification;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import net.java.sip.communicator.plugin.desktoputil.*;
import net.java.sip.communicator.service.systray.*;
import net.java.sip.communicator.service.systray.event.*;
import net.java.sip.communicator.util.*;
/**
* Implements <tt>PopupMessageHandler</tt> using Swing.
*
* @author Symphorien Wanko
* @author Lubomir Marinov
*/
public class PopupMessageHandlerSwingImpl
extends AbstractPopupMessageHandler
{
/** logger for the <tt>PopupMessageHandlerSwingImpl</tt> class */
private static final Logger logger
= Logger.getLogger(PopupMessageHandlerSwingImpl.class);
/**
* Implements <tt>PopupMessageHandler#showPopupMessage()</tt>
*
* @param popupMessage the message we will show
*/
public void showPopupMessage(final PopupMessage popupMessage)
{
if(!SwingUtilities.isEventDispatchThread())
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
showPopupMessage(popupMessage);
}
});
return;
}
final GraphicsConfiguration graphicsConf =
GraphicsEnvironment.getLocalGraphicsEnvironment().
getDefaultScreenDevice().
getDefaultConfiguration();
final JWindow notificationWindow = new JWindow(graphicsConf);
final Timer popupTimer = new Timer(10000, new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
if (notificationWindow.isVisible())
new Thread(new PopupDiscarder(notificationWindow)).start();
}
});
popupTimer.setRepeats(false);
MouseAdapter adapter = new MouseAdapter()
{
@Override
public void mouseEntered(MouseEvent e)
{
popupTimer.stop();
}
@Override
public void mouseExited(MouseEvent e)
{
popupTimer.start();
}
@Override
public void mouseClicked(MouseEvent e)
{
Container container = notificationWindow.getContentPane();
PopupNotificationPanel notif =
(PopupNotificationPanel) container.getComponent(0);
firePopupMessageClicked(
new SystrayPopupMessageEvent(e, notif.getTag()));
notificationWindow.dispose();
}
};
notificationWindow.addMouseListener(adapter);
JComponent content = popupMessage.getComponent();
if (content == null)
{
content = createPopup(
popupMessage.getMessageTitle(),
popupMessage.getMessage(),
popupMessage.getIcon(),
popupMessage.getTag());
}
registerMouseListener(content, adapter);
notificationWindow.add(content);
notificationWindow.setAlwaysOnTop(true);
notificationWindow.pack();
new Thread(new PopupLauncher(notificationWindow, graphicsConf)).start();
popupTimer.start();
}
private void registerMouseListener(Component content, MouseAdapter adapter)
{
content.addMouseListener(adapter);
if(content instanceof JComponent)
for(Component c : ((JComponent) content).getComponents())
registerMouseListener(c, adapter);
}
/**
* Builds the popup component with given informations. Wraps the specified
* <tt>message</tt> in HTML <pre> tags to ensure that text such as
* full pathnames is displayed correctly after HTML is stripped from it.
*
* @param titleString message title
* @param message message content
* @param imageBytes message icon
* @param tag
* @return
*/
private JComponent createPopup( String titleString,
String message,
byte[] imageBytes,
Object tag)
{
JLabel msgIcon = null;
if (imageBytes != null)
{
ImageIcon imageIcon
= ImageUtils.getScaledRoundedIcon(imageBytes, 45, 45);
msgIcon = new JLabel(imageIcon);
}
String plainMessage
= Html2Text.extractText("<pre>" + message + "</pre>");
JTextArea msgContent = new JTextArea(plainMessage);
msgContent.setLineWrap(true);
msgContent.setWrapStyleWord(true);
msgContent.setOpaque(false);
msgContent.setAlignmentX(JTextArea.LEFT_ALIGNMENT);
int msgContentHeight
= getPopupMessageAreaHeight(msgContent, plainMessage);
msgContent.setPreferredSize(new Dimension(250, msgContentHeight));
TransparentPanel notificationBody = new TransparentPanel();
notificationBody.setLayout(
new BoxLayout(notificationBody, BoxLayout.Y_AXIS));
notificationBody.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
notificationBody.add(msgContent);
TransparentPanel notificationContent
= new TransparentPanel();
notificationContent.setLayout(new BorderLayout(5, 0));
notificationContent.setBorder(
BorderFactory.createEmptyBorder(0, 5, 5, 5));
if(msgIcon != null)
notificationContent.add(msgIcon, BorderLayout.WEST);
notificationContent.add(notificationBody, BorderLayout.CENTER);
return new PopupNotificationPanel(titleString, notificationContent, tag);
}
/**
* Implements <tt>toString</tt> from <tt>PopupMessageHandler</tt>
* @return a description of this handler
*/
@Override
public String toString()
{
String applicationName
= SwingNotificationActivator.getResources()
.getSettingsString("service.gui.APPLICATION_NAME");
return SwingNotificationActivator.getResources()
.getI18NString("impl.swingnotification.POPUP_MESSAGE_HANDLER",
new String[]{applicationName});
}
/**
* provide animation to hide a popup. The animation could be described
* as an "inverse" of the one made by <tt>PopupLauncher</tt>.
*/
private static class PopupDiscarder
implements Runnable
{
private final JWindow notificationWindow;
PopupDiscarder(JWindow notificationWindow)
{
this.notificationWindow = notificationWindow;
}
public void run()
{
int height = notificationWindow.getY();
int x = notificationWindow.getX();
do
{
notificationWindow.setLocation(
x,
notificationWindow.getY() + 2);
try
{
Thread.sleep(10);
height -= 2;
} catch (InterruptedException ex)
{
logger.warn("exception while discarding" +
" popup notification window :", ex);
}
} while (height > 0);
notificationWindow.dispose();
}
}
/**
* provide animation to show a popup. The popup comes from the bottom of
* screen and will stay in the bottom right corner.
*/
private static class PopupLauncher
implements Runnable
{
private final JWindow notificationWindow;
private final int x;
private final int y;
PopupLauncher(
JWindow notificationWindow,
GraphicsConfiguration graphicsConf)
{
this.notificationWindow = notificationWindow;
final Rectangle rec = graphicsConf.getBounds();
final Insets ins =
Toolkit.getDefaultToolkit().getScreenInsets(graphicsConf);
x = rec.width + rec.x -
ins.right - notificationWindow.getWidth() - 1;
y = rec.height + rec.y -
ins.bottom - notificationWindow.getHeight() - 1;
notificationWindow.setLocation(x, rec.height);
notificationWindow.setVisible(true);
}
public void run()
{
int height = y - notificationWindow.getY();
do
{
notificationWindow.setLocation(
x,
notificationWindow.getY() - 2);
try
{
Thread.sleep(10);
height += 2;
} catch (InterruptedException ex)
{
logger.warn("exception while showing" +
" popup notification window :", ex);
}
} while (height < 0);
}
}
/**
* Returns the appropriate popup message height, according to the currently
* used font and the size of the message.
*
* @param c the component used to show the message
* @param message the message
* @return the appropriate popup message height
*/
private int getPopupMessageAreaHeight(Component c, String message)
{
int stringWidth = ComponentUtils.getStringWidth(c, message);
int numberOfRows = 0;
if (stringWidth/230 > 5)
numberOfRows = 5;
else
numberOfRows = stringWidth/230 + 1;
FontMetrics fontMetrics = c.getFontMetrics(c.getFont());
return fontMetrics.getHeight()*Math.max(numberOfRows, 3)+5;
}
/**
* Implements <tt>getPreferenceIndex</tt> from <tt>PopupMessageHandler</tt>.
* This handler is able to show images, detect clicks, match a click to a
* message, thus the preference index is 3.
* @return a preference index
*/
public int getPreferenceIndex()
{
return 3;
}
}