/***
 * Windows Tray Icon
 * -----------------
 *
 * Written by Jan Struyf
 *
 *  jan.struyf@cs.kuleuven.ac.be
 *  http://jeans.studentenweb.org/java/trayicon/trayicon.html
 *
 * Please mail me if you
 *	- 've found bugs
 *	- like this program
 *	- don't like a particular feature
 *	- would like something to be modified
 *
 * I always give it my best shot to make a program useful and solid, but
 * remeber that there is absolutely no warranty for using this program as
 * stated in the following terms:
 *
 * THERE IS NO WARRANTY FOR THIS PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE
 * LAW. THE COPYRIGHT HOLDER AND/OR OTHER PARTIES WHO MAY HAVE MODIFIED THE
 * PROGRAM, PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
 * TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
 * PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
 * REPAIR OR CORRECTION.
 *
 * IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL ANY COPYRIGHT HOLDER,
 * OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM,
 * BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
 * CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
 * PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
 * INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE
 * PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER
 * PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 *
 * May the Force be with you... Just compile it & use it!
 */

package demo.swing;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;
import javax.swing.border.*;

import com.jeans.trayicon.*;

// Demo app for Java Tray Icon
public class SwingTrayIcon extends JFrame {    

	public final static int ENABLE_ITEM = 0;
	public final static int BOLD_ITEM = 1;
	public final static int CHECK_ITEM = 2;

	protected TrayIconButton[] m_Icon;
	protected boolean m_JAWTInit;
	protected JCheckBox m_SwingMenu, m_UseChinese;
	protected JButton m_ExitButton, m_HideButton, m_AboutButton;	
	protected SwingTrayPopup[] m_Popup = new SwingTrayPopup[4];
	// protected Font m_Font = new Font("MS Song", Font.PLAIN, 12);

	public SwingTrayIcon() throws TrayIconException, InterruptedException {
		super("SwingTrayIcon");

		// Set callback method to send windows messages through Tray Icon library
		// (see WindowsMessageCallback)
		WindowsTrayIcon.setWindowsMessageCallback(new WindowsMessageCallback());

		// Create the icon
		JPanel panel = new JPanel();
		panel.setLayout(new GridLayout(0,1,3,3));		
		setIconImage(loadImage("Duke16.gif"));

		// Create 4 checkboxes with icons: Printer, Battery, France & MS-DOS
		m_Icon = new TrayIconButton[4];
		panel.add(m_Icon[0] = new TrayIconButton("Duke",loadImage("Duke16.gif"),16,16));		
		panel.add(m_Icon[1] = new TrayIconButton("Printer",loadImage("Printer.gif"),16,16));
		panel.add(m_Icon[2] = new TrayIconButton("MS-DOS",loadImage("MsDos.gif"),16,16));
		panel.add(m_Icon[3] = new TrayIconButton("France",loadImage("France.gif"),16,16));		

		// Tray Icon left mouse button event restores the main window
		// (make it visible again/requests focus)
		RestoreListener listener = new RestoreListener(false);
		TestMouseListener mouse = new TestMouseListener();
		for (int k = 0; k < 4; k++) {
			m_Icon[k].addActionListener(listener);
			m_Icon[k].addMouseListener(mouse);
		}
		
		// Each icon has it's own popup menu
		// Because the menu's contain state information, you can't use the same menu for different icons)
		m_Icon[0].setPopup(makePopup());
		m_Icon[1].setPopup(makePopup());
		m_Icon[2].setPopup(makePopup());
		m_Icon[3].setPopup(makePopup());		

		// Panel with exit, hide & about button - With hide you can hide the main window and restore it
		// by clicking on one of the Tray Icons
		JCheckBox m_SwingMenu = new JCheckBox("Use Swing menu");
		m_SwingMenu.addItemListener(new MySwingMenuListener());
		panel.add(m_SwingMenu);				
		//m_UseChinese = new JCheckBox("Display menu in Chinese");
		//m_UseChinese.addItemListener(new MySwingMenuListener());
		//panel.add(m_UseChinese);
				
		JPanel buttons = new JPanel();
		buttons.setLayout(new GridLayout(1,0,3,3));
		buttons.add(m_ExitButton = new JButton("Exit"));
		m_ExitButton.addActionListener(new ExitListener());
		buttons.add(m_HideButton = new JButton("Hide"));
		m_HideButton.addActionListener(new HideListener());
		buttons.add(m_AboutButton = new JButton("About"));
		m_AboutButton.addActionListener(new AboutListener());
		panel.add(buttons);
		panel.setBorder(new EmptyBorder(3,3,3,3));
		setContentPane(panel);
		addWindowListener(new WindowClosingListener());
		pack();
	}
	
	// Create the popup menu for each Tray Icon (on right mouse click)
	public TrayIconPopup makePopup() {
		// Make new popup menu
		TrayIconPopup popup = new TrayIconPopup();
		
		// Add show, about, submenu, separator & exit item
		TrayIconPopupSimpleItem item = new TrayIconPopupSimpleItem("&Show");
		item.setDefault(true);
		
		// Each menu item can have it's own ActionListener		
		item.addActionListener(new RestoreListener(true));
		popup.addMenuItem(item);
		item = new TrayIconPopupSimpleItem("&About");
		item.addActionListener(new AboutListener());
		popup.addMenuItem(item);
		
		// Create a submenu with title enable and items check 1 & check 2
		TrayIconPopup sub = new TrayIconPopup("&Options");
		
		// Create and add some checkbox menu items
		TrayIconPopupCheckItem test = new TrayIconPopupCheckItem("Test");
		sub.addMenuItem(test);
		sub.addMenuItem(new TrayIconPopupSeparator());
		TrayIconPopupCheckItem ena = new TrayIconPopupCheckItem("Enable Test");
		ena.addActionListener(new ChangeMenuListener(test, ENABLE_ITEM));			
		ena.setCheck(true);
		sub.addMenuItem(ena);
		TrayIconPopupCheckItem bold = new TrayIconPopupCheckItem("Bold Test");
		bold.addActionListener(new ChangeMenuListener(test, BOLD_ITEM));
		sub.addMenuItem(bold);
		TrayIconPopupCheckItem check = new TrayIconPopupCheckItem("Check Test");
		check.addActionListener(new ChangeMenuListener(test, CHECK_ITEM));
		test.addActionListener(new ChangeMenuListener(check, CHECK_ITEM));
		sub.addMenuItem(check);		
		// Add submenu to the main menu
		popup.addMenuItem(sub);
		// Add a separator		
		popup.addMenuItem(new TrayIconPopupSeparator());
		// Add exit item
		item = new TrayIconPopupSimpleItem("E&xit");
		item.addActionListener(new ExitListener());
		popup.addMenuItem(item);
		return popup;
	}
	
	public SwingTrayPopup makeSwingPopup() {
		SwingTrayPopup popup = new SwingTrayPopup();
		// popup.setFont(m_Font);
		JMenuItem item = new JMenuItem(TR.get(TR.SHOW), new ImageIcon(loadImage("Duke16.gif")));				
		// Each menu item can have it's own ActionListener		
		item.addActionListener(new RestoreListener(true));
		// item.setFont(m_Font);
		popup.add(item);
		item = new JMenuItem(TR.get(TR.ABOUT), new ImageIcon(loadImage("About.gif")));	
		item.addActionListener(new AboutListener());
		// item.setFont(m_Font);
		popup.add(item);
		// Create a submenu with title enable and items check 1 & check 2
		JMenu sub = new JMenu(TR.get(TR.OPTIONS));
		// sub.setFont(m_Font);
		// Create and add some checkbox menu items
		JCheckBoxMenuItem test = new JCheckBoxMenuItem(TR.get(TR.TEST));
		test.setEnabled(false);
		// test.setFont(m_Font);
		sub.add(test);
		sub.addSeparator();
		JCheckBoxMenuItem ena = new JCheckBoxMenuItem(TR.get(TR.EN_TEST));
		ena.addItemListener(new SwingChangeMListener(test, ENABLE_ITEM));
		ena.setState(true);
		// ena.setFont(m_Font);
		sub.add(ena);
		JCheckBoxMenuItem bold = new JCheckBoxMenuItem(TR.get(TR.BL_TEST));
		bold.addItemListener(new SwingChangeMListener(test, BOLD_ITEM));		
		bold.setEnabled(false);
		// bold.setFont(m_Font);
		sub.add(bold);
		JCheckBoxMenuItem check = new JCheckBoxMenuItem(TR.get(TR.CH_TEST));
		check.addItemListener(new SwingChangeMListener(test, CHECK_ITEM));		
		test.addItemListener(new SwingChangeMListener(check, CHECK_ITEM));		
		// check.setFont(m_Font);
		sub.add(check);		
		// Add submenu to the main menu
		popup.add(sub);
		// Add a separator		
		popup.addSeparator();
		// Add exit item
		item = new JMenuItem(TR.get(TR.EXIT));
		item.addActionListener(new ExitListener());
		// item.setFont(m_Font);
		popup.add(item);
		return popup;	        
	}
		
	// Load a gif image (used for loading the 16x16 icon gifs)
	public static Image loadImage(String fileName) {
		return Toolkit.getDefaultToolkit().getImage("demo"+File.separator+"images"+File.separator+fileName);
	}

	public static void setLookAndFeel() {
		try {
			UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
		} catch (Exception ex) {}
	}    	

	// Main proc
	public static void main(String[] args) {	    
		try {
			setLookAndFeel();
			TR.load("demo"+File.separator+"swing"+File.separator+"menu.eng");
			String appName = "SwingTray";

			// First check if there's another instance of our app running by sending a windows
			// message to a hidden icon window "TestTray" - each Tray Icon app has a hidden window
			// that receives the mouse/menu messages for it's Tray Icons
			long result = WindowsTrayIcon.sendWindowsMessage(appName, 1234);
			if (result != -1) {
				// If window exists, there's already an instance of our app running
				// Print message and exit (other app will restore its window when receiving
				// our message - see WindowsMessageCallback
				System.out.println("[Already running other instance of "+appName+" (returns: "+result+")].");
				return;
			}
			// Init the Tray Icon library given the name for the hidden window
			WindowsTrayIcon.initTrayIcon(appName);
			// Show the icon
			SwingTrayIcon tray = new SwingTrayIcon();
			SwingTrayIcon.centerDialog(tray);
			tray.setVisible(true);
		} catch (TrayIconException e) {
			System.out.println("Error: "+e.getMessage());
		} catch (IOException e) {
			System.out.println("Error: "+e.getMessage());
		} catch (InterruptedException e) { }
	}
	
	// If you want to use the Swing menu, you should initialize JAWT first.
	// The conditional is there so that initJAWT is only called once, when needed.
	// See README file for more information on JAWT problems.
	public void initJAWT() {
		if (!m_JAWTInit) {
			WindowsTrayIcon.initJAWT();
			m_JAWTInit = true;
		}
	}

	public void doExit() {
		System.out.println("[Exit selected / Close requested].");
		
		// Free all Tray Icon resources - always call this on exit
		WindowsTrayIcon.cleanUp();
		
		// Exit application
		System.exit(0);
	}


	
	public void doHide(boolean exitOnFail) {
		System.out.println("[Hide selected].");

		// Check if there's a Tray Icon visible
		// Hide works only when there's an icon in the system tray
		boolean visible = true;
		for (int k = 0; k < 4; k++) 
			if (m_Icon[k].testVisible()) visible = false;
		setVisible(visible);
		if (visible == true) {
			System.out.println("[Hide works only when there's an icon in the system tray].");
			if (exitOnFail) doExit();
		}
	}
    
	public void doUpdateMenu(boolean awtSwing, boolean engZh) {
		if (awtSwing) {            
			for (int i = 0; i < 4; i++) {
				m_Icon[i].setPopup(makePopup());
				m_Popup[i].setTrayIcon(null);
			}            
		} else {            
			initJAWT();    
			for (int i = 0; i < 4; i++) {
				m_Icon[i].setPopup(null);
				if (m_Popup[i] == null) m_Popup[i] = makeSwingPopup();
				m_Icon[i].setSwingPopup(m_Popup[i]);
			}
		}
	}
        
	public static void centerDialog(Window frame) {
		Dimension dialogSize = frame.getSize();
		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
		frame.setLocation(screenSize.width/2 - dialogSize.width/2,
		                  screenSize.height/2 - dialogSize.height/2);
	}        

	// Callback listener handles incoming windows messages. In this demo, a windows message is sent
	// when the user tries to start a second instance of the demo app. You can try this one by opening
	// two MS-DOS prompts and say in each one "java demo.swing.SwingTrayIcon"
	// MS-DOS 1:
	//      C:\TrayIcon>java demo.swing.SwingTrayIcon
	//      ...
	//      Other instance started (parameter: 1234)
	//
	// MS-DOS 2:
	//      C:\TrayIcon>java demo.swing.SwingTrayIcon
	//      Already running other instance of TestTray (returns: 4321)
	private class WindowsMessageCallback implements TrayIconCallback {

		public int callback(int param) {
			// Param contains the integer value send with sendWindowsMessage(appName,param)
			System.out.println("[Other instance started (parameter: "+param+")].");
			setVisible(true); toFront(); requestFocus();
			
			// Return integer value to other process
			return 4321;
		}
	}
	
	// Callback listener handles exit button / exit popup menu
	private class ExitListener implements ActionListener {

		public void actionPerformed(ActionEvent evt) {
			doExit();
		}
	}
	
	public class WindowClosingListener extends WindowAdapter {
	
		public void windowClosing(WindowEvent e) {
			doHide(true);
		}
	}
		
	// Callback listener for hide button
	private class HideListener implements ActionListener {

		public void actionPerformed(ActionEvent evt) {
			doHide(false);			
		}
	}

	// Callback listener handles about button
	private class AboutListener implements ActionListener {

		public void actionPerformed(ActionEvent evt) {
			System.out.println("[About selected].");
			// TestTrayIcon.this instead of this$0 for Java 1.3 compatibility
			AboutBox box = new AboutBox(SwingTrayIcon.this);
			centerDialog(box);
			box.show();
		}
	}	
	
	// Callback listener handles restore (click left on any icon / show popup menu)
	private class RestoreListener implements ActionListener {
		
		protected boolean from_menu;
	
		public RestoreListener(boolean fromMenu) {
			from_menu = fromMenu;
		}

		public void actionPerformed(ActionEvent evt) {
			if (from_menu) {
				setVisible(true);
				toFront(); requestFocus();
			} else if (evt.getActionCommand().equals("Left")) {
				if (!isVisible()) {
					// Make main window visible if it was hidden
					setVisible(true);
					// Request input focus
					toFront(); requestFocus();
				} else {
					doHide(false);
				}
			}
		}
	}
	
	// Callback listener handles restore (click left on any icon / show popup menu)
	private class ChangeMenuListener implements ActionListener {

		protected TrayIconPopupCheckItem m_Item;
		protected int m_Change;

		public ChangeMenuListener(TrayIconPopupCheckItem item, int change) {
			m_Item = item;
			m_Change = change;
		}

		public void actionPerformed(ActionEvent evt) {
			TrayIconPopupCheckItem source = (TrayIconPopupCheckItem)evt.getSource();
			boolean value = source.getCheck();
			switch (m_Change) {
			    case ENABLE_ITEM:
			        m_Item.setEnabled(value); break;
			    case BOLD_ITEM:
			        m_Item.setDefault(value); break;
			    case CHECK_ITEM:
			        m_Item.setCheck(value); break;
			}
		}
	}	
	
	// Callback listener handles restore (click left on any icon / show popup menu)
	private class SwingChangeMListener implements ItemListener {

		protected JCheckBoxMenuItem m_Item;
		protected int m_Change;

		public SwingChangeMListener(JCheckBoxMenuItem item, int change) {
			m_Item = item;
			m_Change = change;
		}

		public void itemStateChanged(ItemEvent e) {
			boolean value = e.getStateChange() == ItemEvent.SELECTED;
			switch (m_Change) {
			    case ENABLE_ITEM:
			        m_Item.setEnabled(value); break;
/*			    case BOLD_ITEM:
			        m_Item.setDefault(value); break;*/
			    case CHECK_ITEM:
			        m_Item.setState(value); break;
			}
		}
	}	
			
	public class MySwingMenuListener implements ItemListener {
        
		public void itemStateChanged(ItemEvent e) {
			boolean chinese = false; // m_UseChinese.getState();
			if (e.getStateChange() == ItemEvent.SELECTED) {
				doUpdateMenu(false, !chinese);
			} else {                
				doUpdateMenu(true, !chinese);                
			}
		}    
	}
    
	public class TestMouseListener extends MouseAdapter {
    
		public void mousePressed(MouseEvent evt) {
			if ((evt.getModifiers() & MouseEvent.BUTTON1_MASK) != 0 && evt.getClickCount() == 2) {
				System.out.println("[Tray icon double clicked].");
			}
		}
	}
}

// Stupid about box for demo app
class AboutBox extends JDialog {

	// Create new about box given parent frame
	public AboutBox(JFrame parent)  {
		// Make modal dialog given parent and title
		super(parent, "About TrayIcon", true);
		// Layout stuff
		JPanel panel = new JPanel();
		panel.setLayout(new BorderLayout(3,3));
		JPanel txt = new JPanel();
		txt.setLayout(new GridLayout(0,1));
		txt.add(new JLabel("TrayIcon version "+WindowsTrayIcon.TRAY_VERSION));
		txt.add(new JLabel("Written by Jan Struyf <Jan.Struyf@cs.kuleuven.ac.be>"));
		panel.add(txt, BorderLayout.CENTER);
		JPanel buttons = new JPanel();
		buttons.setLayout(new FlowLayout());
		JButton button = new JButton("OK");
		buttons.add(button, BorderLayout.CENTER);
		panel.add(buttons, BorderLayout.SOUTH);
		// Close listeners for OK button and window button
		button.addActionListener(new CloseListener());
		addWindowListener(new CloseWindowListener());
		panel.setBorder(new EmptyBorder(2,2,2,2));
		setContentPane(panel);
		pack();		
	}

	// Close listener for OK button
	private class CloseListener implements ActionListener {

		public void actionPerformed(ActionEvent evt) {
			dispose();
		}
	}

	// Close listener for windows button
	private class CloseWindowListener extends WindowAdapter {

		public void windowClosing(WindowEvent evt) {
			dispose();
		}
	}
}

// Panel with checkbox, string and icon
class TrayIconButton extends JPanel {

	// The checkbox for enabling this Tray Icon
	JCheckBox button;
	// The Tray Icon's class
	WindowsTrayIcon icon;

	// Create new panel with checkbox, string and icon
	// Name = Printer/France/Battery/MS-DOS
	// Image = 16x16 Icon gif
	// Wd = 16, Hi = 16
	public TrayIconButton(String name, Image image, int wd, int hi) throws TrayIconException, InterruptedException {
		setLayout(new BorderLayout(3,3));
		// Add icon image in ImageDisplayer
		add(new JLabel(new ImageIcon(image)), BorderLayout.EAST);
		// Add checkbox for enabling Tray Icon
		add(button = new JCheckBox(name), BorderLayout.CENTER);
		button.addItemListener(new ToggleListener());
		// Create Tray Icon and set tooltip
		icon = new WindowsTrayIcon(image, wd, hi);
		icon.setToolTipText(name);
	}

	// Add popup menu to Tray Icon	
	public void setSwingPopup(SwingTrayPopup popup) {	    
		popup.setTrayIcon(icon);
	}

	// Add popup menu to Tray Icon
	public void setPopup(TrayIconPopup popup) {
		icon.setPopup(popup);
	}

	// Add action listener to Tray Icon
	public void addActionListener(ActionListener listener) {
		icon.addActionListener(listener);
	}
	
	// Add mouse listener to Tray Icon
	public void addMouseListener(MouseListener mouse) {
		icon.addMouseListener(mouse);
	}

	// Icon visible?
	public boolean testVisible() {
		return icon.isVisible();
	}

	// Listener for checkbox
	// Make icon visible/invisible depending on checkbox state
	private class ToggleListener implements ItemListener {

		public void itemStateChanged(ItemEvent evt) {
			icon.setVisible(evt.getStateChange() == ItemEvent.SELECTED);
		}

	}
}
