package org.fenrir.yggdrasil.ui;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.JDialog;
import javax.swing.AbstractAction;
import javax.swing.event.MouseInputAdapter;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.fenrir.yggdrasil.ui.action.Action;
import org.fenrir.yggdrasil.ui.event.INotificationActionSupport;
import org.fenrir.yggdrasil.ui.widget.TrayNotificationWidget;
import org.fenrir.yggdrasil.ui.widget.TrayPopup;
import org.fenrir.yggdrasil.ui.widget.TrayPopupEntry;
import org.fenrir.yggdrasil.core.event.IApplicationNotification;
import org.fenrir.yggdrasil.core.event.IApplicationNotificationListener;
import org.fenrir.yggdrasil.core.event.annotation.EventListener;
/**
* TODO v1.0 Documentació
* @author Antonio Archilla Nava
* @version v0.1.20131215
*/
@EventListener(definitions=IApplicationNotificationListener.class)
public class ApplicationTray extends JPanel implements IApplicationNotificationListener
{
private static final long serialVersionUID = -3431599851638474183L;
private final Logger log = LoggerFactory.getLogger(ApplicationTray.class);
private TrayPopup trayPopup;
private Box trayZone;
private Map<String, TrayNotificationWidget> trayZoneElements = new HashMap<String, TrayNotificationWidget>();
private Timer clearNotificationTimer;
private JDialog notificationDialog;
public ApplicationTray()
{
createContents();
}
private void createContents()
{
trayPopup = new TrayPopup(this);
trayPopup.setSize(350, 400);
trayPopup.addWindowFocusListener(new WindowFocusListener()
{
@Override
public void windowGainedFocus(WindowEvent event)
{
// No s'ha de fer res
}
@Override
public void windowLostFocus(WindowEvent event)
{
((JDialog)event.getSource()).setVisible(false);
}
});
this.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, new Color(200, 200, 200)));
this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
// Espai restant
this.add(Box.createHorizontalGlue());
// Àrea del tray
trayZone = Box.createHorizontalBox();
this.add(trayZone);
// Alçada mínima i marge dret
this.add(Box.createRigidArea(new Dimension(25, 25)));
}
@Override
public void singleNotification(IApplicationNotification notification)
{
// Entrada al popup
final TrayPopupEntry entry = new TrayPopupEntry();
entry.setNotificationId(notification.getId());
entry.setMessage(notification.getMessage());
if(notification instanceof INotificationActionSupport){
Action action = ((INotificationActionSupport)notification).getAction();
entry.setAction(action);
}
ImageIcon icon = new ImageIcon(getClass().getResource(notification.getIconPath()));
entry.setIcon(icon);
final boolean showNotification = ApplicationWindowManager.getInstance().isMainWindowActive();
if(!showNotification){
if(log.isDebugEnabled()){
log.debug("Finestra no visible; Mostrant notificació al tray");
}
ApplicationWindowManager.getInstance().displaySystemTrayMessage(notification.getCaption(), notification.getMessage(), TrayIcon.MessageType.INFO);
}
if(SwingUtilities.isEventDispatchThread()){
addNotification(entry, showNotification);
}
else{
try{
SwingUtilities.invokeAndWait(new Runnable()
{
@Override
public void run()
{
addNotification(entry, showNotification);
}
});
}
catch(InterruptedException e){
log.error("Error mostrant notificació: {}", e.getMessage(), e);
}
catch(InvocationTargetException e){
log.error("Error mostrant notificació: {}", e.getMessage(), e);
}
}
}
private void addNotification(TrayPopupEntry entry, boolean showNotification)
{
trayPopup.addEntry(entry);
// Icona al tray
TrayNotificationWidget trayLabel;
if(trayZoneElements.containsKey(entry.getNotificationId())){
trayLabel = trayZoneElements.get(entry.getNotificationId());
}
else{
trayLabel = new TrayNotificationWidget(entry.getIcon());
trayZone.add(trayLabel.getWidget());
trayZoneElements.put(entry.getNotificationId(), trayLabel);
trayLabel.addMouseListener(new MouseInputAdapter()
{
@Override
public void mouseClicked(MouseEvent event)
{
showMessagesPopup();
}
});
}
trayLabel.incrementNotificationCount();
trayZone.revalidate();
// Necessari per reflectir els canvis del contador
trayZone.repaint();
// Si la finestra principal de l'aplicació es visible es motrarà la notificació
if(showNotification){
if(notificationDialog!=null && notificationDialog.isVisible()){
notificationDialog.setVisible(false);
notificationDialog.dispose();
notificationDialog = null;
}
notificationDialog = createNotification(entry.getIcon(), entry.getMessage());
notificationDialog.setVisible(true);
notificationDialog.requestFocusInWindow();
// Timer per esborrar el missatge de l'àrea
if(clearNotificationTimer==null){
// Delay 5seg.
clearNotificationTimer = new Timer(5000, new AbstractAction()
{
@Override
public void actionPerformed(ActionEvent event)
{
if(notificationDialog!=null){
notificationDialog.setVisible(false);
notificationDialog.dispose();
notificationDialog = null;
}
}
});
clearNotificationTimer.start();
}
else{
clearNotificationTimer.restart();
}
}
}
private void showMessagesPopup()
{
JFrame appWindow = ApplicationWindowManager.getInstance().getMainWindow();
Point appLocation = appWindow.getLocation();
Dimension appDimension = appWindow.getSize();
int posX = appLocation.x + appDimension.width - trayPopup.getSize().width - 10;
int posY = appLocation.y + appDimension.height - trayPopup.getSize().height - 35;
trayPopup.setLocation(new Point(posX, posY));
trayPopup.setVisible(true);
trayPopup.requestFocusInWindow();
}
private JDialog createNotification(Icon icon, String message)
{
JDialog dialog = new JDialog();
dialog.setUndecorated(true);
dialog.setVisible(false);
Box box = Box.createHorizontalBox();
// Directament no es pot afegir border a la finestra de dialeg, pel que es fa al panell de contingut
box.setBorder(BorderFactory.createLineBorder(Color.BLACK));
// Label icona
JLabel lIcon = new JLabel(icon);
lIcon.setMinimumSize(new Dimension(25, 25));
lIcon.setPreferredSize(new Dimension(25, 25));
lIcon.setMaximumSize(new Dimension(25, 25));
box.add(lIcon);
box.add(Box.createHorizontalStrut(10));
// Label message
String htmlMessage = "<html>" + message.replaceAll("\n", "<br/>") + "</html>";
JLabel lMessage = new JLabel(htmlMessage);
box.add(lMessage);
// Espai extra
box.add(Box.createHorizontalGlue());
// S'afegeix el contingut
dialog.getContentPane().add(box);
// Tamany
dialog.setSize(320, box.getPreferredSize().height + 20);
// Posició
JFrame appWindow = ApplicationWindowManager.getInstance().getMainWindow();
Point appLocation = appWindow.getLocation();
Dimension appDimension = appWindow.getSize();
int posX = appLocation.x + appDimension.width - dialog.getSize().width - 10;
int posY = appLocation.y + appDimension.height - dialog.getSize().height - 35;
dialog.setLocation(new Point(posX, posY));
return dialog;
}
public void discardAllNotifications()
{
trayZone.removeAll();
trayZone.revalidate();
trayPopup.discardAllNotifications();
trayZoneElements.clear();
}
public void discardNotification(TrayPopupEntry notificacionEntry)
{
TrayNotificationWidget widget = trayZoneElements.get(notificacionEntry.getNotificationId());
widget.decrementNotificationCount();
// Només es treu la notificació del tray quan el contador del mateix tipus sigui <= 0
if(widget.getNotificationCount()<=0){
JComponent label = trayZoneElements.remove(notificacionEntry.getNotificationId()).getWidget();
trayZone.remove(label);
}
// Es repinta la zona per mostrar el canvi
trayZone.revalidate();
trayZone.repaint();
trayPopup.discardNotification(notificacionEntry);
}
}