3

enter image description here

Sorry if the title was vague but this is what I am trying to implement.
This is Battery Care software iconified / minimized. When you mouse over the icon, you get the window you see in the picture.
How can that be implemented in Java?

Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
An SO User
  • 24,612
  • 35
  • 133
  • 221
  • 1
    Maybe you can start with [this](http://stackoverflow.com/questions/1882055/java-swing-change-background-color-on-mouse-over) – Jefferson Jan 17 '13 at 02:20
  • 1
    Start by having a look at [How to use the System Tray](http://docs.oracle.com/javase/tutorial/uiswing/misc/systemtray.html) – MadProgrammer Jan 17 '13 at 02:20
  • 1
    Is it specific about system tray? Or you just want to know about onMouseOver event? For system tray feature you can see http://docs.oracle.com/javase/tutorial/uiswing/misc/systemtray.html – Jon Kartago Lamida Jan 17 '13 at 02:20
  • @JonKartagoLamida Specific for system tray. – An SO User Jan 17 '13 at 02:20
  • Once you have had a look at the tutorials, I'd have a look at [JPopupMenu in TrayIcon](http://weblogs.java.net/blog/ixmal/archive/2006/05/using_jpopupmen.html) – MadProgrammer Jan 17 '13 at 02:23
  • @MadProgrammer working on that as well as not being able to play sound in a jar file – An SO User Jan 17 '13 at 02:24
  • 1
    @LittleChild I was thinking more along the lines of once you've had a look at "How to use System Tray" tutorial, then look at the "JPopupMenu in TrayIcon" as it might be of interest – MadProgrammer Jan 17 '13 at 02:39

2 Answers2

7

enter image description here

This uses a JPopupMenu to display information on a click, not the most recommended approach, as it's difficult to layout other components...but you could just as easily use a JWindow instead...

public class SystemTrayTest {

  public static void main(String[] args) {
    if (SystemTray.isSupported()) {

      EventQueue.invokeLater(new Runnable() {
        @Override
        public void run() {
          try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
          } catch (Exception ex) {
          }
          try {
            final JPopupMenu popup = new JPopupMenu();
            popup.add(new JLabel("Charging (45%)", JLabel.CENTER));

            popup.add(new JLabel("Charging", new ImageIcon(ImageIO.read(SystemTrayTest.class.getResource("/battery_connection.png"))), JLabel.LEFT));
            popup.add(new JLabel("Power Saver", new ImageIcon(ImageIO.read(SystemTrayTest.class.getResource("/flash_yellow.png"))), JLabel.LEFT));

            popup.add(new JSeparator());

            JMenuItem exitMI = new JMenuItem("Exit");
            exitMI.addActionListener(new ActionListener() {
              @Override
              public void actionPerformed(ActionEvent e) {
                System.exit(0);
              }
            });
            popup.add(exitMI);

            TrayIcon trayIcon = new TrayIcon(ImageIO.read(SystemTrayTest.class.getResource("/battery_green.png")), "Feel the power");
            trayIcon.addMouseListener(new MouseAdapter() {
              @Override
              public void mouseClicked(MouseEvent e) {
                popup.setLocation(e.getX(), e.getY());
                popup.setInvoker(popup);
                popup.setVisible(true);
              }
            });
            SystemTray.getSystemTray().add(trayIcon);
          } catch (Exception ex) {
            ex.printStackTrace();
            System.exit(0);
          }

        }
      });
    }

  }
}

Updated with mouse over support

Cause I can't resist playing...

enter image description here

public class SystemTrayTest {

  public static void main(String[] args) {
    new SystemTrayTest();
  }

  public SystemTrayTest() {
    if (SystemTray.isSupported()) {

      EventQueue.invokeLater(new Runnable() {
        @Override
        public void run() {
          try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
          } catch (Exception ex) {
          }
          try {
            TrayIcon trayIcon = new TrayIcon(ImageIO.read(SystemTrayTest.class.getResource("/battery_green.png")), "Feel the power");
            MouseHandler mouseHandler = new MouseHandler();
            trayIcon.addMouseMotionListener(mouseHandler);
            trayIcon.addMouseListener(mouseHandler);
            SystemTray.getSystemTray().add(trayIcon);
          } catch (Exception ex) {
            ex.printStackTrace();
            System.exit(0);
          }
        }
      });
    }
  }

  public class MouseHandler extends MouseAdapter {

    private Timer popupTimer;
    private JWindow popup;
    private Point point;

    public MouseHandler() {
      popup = new JWindow();
      ((JComponent)popup.getContentPane()).setBorder(new LineBorder(Color.LIGHT_GRAY));
      popup.setLayout(new GridLayout(0, 1));
      popup.add(new JLabel("Charging (45%)", JLabel.CENTER));

      try {
        popup.add(new JLabel("Charging", new ImageIcon(ImageIO.read(getClass().getResource("/battery_connection.png"))), JLabel.LEFT));
        popup.add(new JLabel("Power Saver", new ImageIcon(ImageIO.read(getClass().getResource("/flash_yellow.png"))), JLabel.LEFT));
      } catch (IOException exp) {
        exp.printStackTrace();
      }
      popup.pack();

      popupTimer = new Timer(250, new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
          if (point != null) {
            System.out.println(point);
            Rectangle bounds = getScreenViewableBounds(point);
            int x = point.x;
            int y = point.y;
            if (y < bounds.y) {
              y = bounds.y;
            } else if (y > bounds.y + bounds.height) {
              y = bounds.y + bounds.height;
            }
            if (x < bounds.x) {
              x = bounds.x;
            } else if (x > bounds.x + bounds.width) {
              x = bounds.x + bounds.width;
            }

            if (x + popup.getWidth() > bounds.x + bounds.width) {
              x = (bounds.x + bounds.width) - popup.getWidth();
            }
            if (y + popup.getWidth() > bounds.y + bounds.height) {
              y = (bounds.y + bounds.height) - popup.getHeight();
            }
            popup.setLocation(x, y);
            popup.setVisible(true);
          }
        }
      });
      popupTimer.setRepeats(false);

    }

    @Override
    public void mouseExited(MouseEvent e) {
      System.out.println("Stop");
      point = null;
      popupTimer.stop();
      popup.setVisible(false);
    }

    @Override
    public void mouseMoved(MouseEvent e) {
      popupTimer.restart();
      point = e.getPoint();
    }

    @Override
    public void mouseClicked(MouseEvent e) {
      System.exit(0);
    }
  }

  public static GraphicsDevice getGraphicsDeviceAt(Point pos) {

    GraphicsDevice device = null;

    GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
    GraphicsDevice lstGDs[] = ge.getScreenDevices();

    ArrayList<GraphicsDevice> lstDevices = new ArrayList<GraphicsDevice>(lstGDs.length);

    for (GraphicsDevice gd : lstGDs) {

      GraphicsConfiguration gc = gd.getDefaultConfiguration();
      Rectangle screenBounds = gc.getBounds();

      if (screenBounds.contains(pos)) {

        lstDevices.add(gd);

      }

    }

    if (lstDevices.size() == 1) {

      device = lstDevices.get(0);

    }

    return device;

  }

  public static Rectangle getScreenViewableBounds(Point p) {

    return getScreenViewableBounds(getGraphicsDeviceAt(p));

  }

  public static Rectangle getScreenViewableBounds(GraphicsDevice gd) {

    Rectangle bounds = new Rectangle(0, 0, 0, 0);

    if (gd != null) {

      GraphicsConfiguration gc = gd.getDefaultConfiguration();
      bounds = gc.getBounds();

      Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(gc);

      bounds.x += insets.left;
      bounds.y += insets.top;
      bounds.width -= (insets.left + insets.right);
      bounds.height -= (insets.top + insets.bottom);

    }

    return bounds;

  }
}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • `JWindow` is basically like a `JPanel` that can be shown on the screen, correct ? – An SO User Jan 17 '13 at 08:54
  • No, not really. `JWindow` is more like an undecorated `JFrame` (`JFrame` extends from `JWindow`) – MadProgrammer Jan 17 '13 at 09:02
  • I knew the hierarchy, was just giving a simile :) If you are still in the mood for some more Java: http://stackoverflow.com/questions/14375338/cannot-play-embedded-sound-in-a-jar-file – An SO User Jan 17 '13 at 09:03
3

At first, I thought this would be fairly trivial - add a mouse listener to the TrayIcon using addMouseListener() and use the mouseEntered() and mouseExitedEvents():

icon.addMouseListener(new MouseAdapter()
{
    @Override
    public void mouseEntered(MouseEvent e)
    {
        System.out.println("Mouse over icon");
    }

    @Override
    public void mouseExited(MouseEvent e)
    {
        System.out.println("Mouse leaving icon");
    }
});

But, at least on the platform I tested with (Java 7u10 on Windows) this did not work - no events were generated when I hovered the mouse cursor over the tray icon. What did work was a MouseMotionListener and the addMouseMotionListener method:

icon.addMouseMotionListener(new MouseMotionAdapter()
{
    @Override
    public void mouseMoved(MouseEvent e)
    {
        System.out.println("Moving...");
    }
});

Events are generated in mouseMoved() only when the mouse is over the icon. With a bit of work, this could be used to display a JWindow after a certain delay after first event is received to emulate tooltip time (you can get the platform's tooltip display time through something like ToolTipManager.sharedInstance().getInitialDelay().

prunge
  • 22,460
  • 3
  • 73
  • 80