I'm trying to create system tray icon with two popup menus, one should be called with left mouse button and another with right. AWT offers simple way to create PopupMenu but it's not posible to call menu with left mouse button without making invisible JFrame or something. So, I find out that swing JPopupMenu can be called by any of mouse buttons. But JPopupMenu have bug (I don't know if this is really a bug or I'm not so good in Java) that it's not hiding when I press mouse outside JPopupMenu. I've tried to use mouse listener's function mouseExited, but it works only on JPopupMenu border. If mouse leaves JPopupMenu border it hides and I can't press any of JPopupMenu buttons. Maybe, anyone had the same problem and could help me find out how to make it work right.
public static JPopupMenu jpm;
public static TrayIcon ti;
public static void main(String args[]) throws IOException, AWTException,
ClassNotFoundException, InstantiationException,
IllegalAccessException, UnsupportedLookAndFeelException {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
SystemTray st = SystemTray.getSystemTray();
Image icon = ImageIO.read(SipLogin.class
.getResource("/resources/phone-yellow-small.png"));
ti = new TrayIcon(icon, "Sip login", null);
jpm = new JPopupMenu();
JMenuItem jmi1 = new JMenuItem("JMenuItem1");
JMenuItem jmi2 = new JMenuItem("JMenuItem2");
JMenuItem jmi3 = new JMenuItem("JMenuItem3");
JMenuItem jmi4 = new JMenuItem("JMenuItem4");
ti.addMouseListener(new MouseAdapter() {
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger()) {
jpm.setLocation(e.getX(), e.getY());
jpm.setInvoker(jpm);
jpm.setVisible(true);
}
}
});
jpm.add(jmi1);
jpm.add(jmi2);
jpm.add(jmi3);
jpm.add(jmi4);
jpm.addMouseListener(new MouseAdapter() {
public void mouseExited(MouseEvent e) {
jpm.setVisible(false);
}
});
st.add(ti);
}
Take that one mouse listener and separate the left and right click:
Change from
ti.addMouseListener(new MouseAdapter() {
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger()) {
jpm.setLocation(e.getX(), e.getY());
jpm.setInvoker(jpm);
jpm.setVisible(true);
}
}
});
to
ti.addMouseListener(new MouseAdapter() {
public void mouseReleased(MouseEvent e) {
if(e.getclickCount < 2){
if(e.getButton().equals(MouseEvent.Button1){
showPopup1();
}
if(e.getButton().equals(MouseEvent.Button3){
showPopup2();
}
}
}
});
thanks for very useful conversation, but I need to add some words:
Sometimes when you add menuItems the JPopupMenu behaves in unpredictable manner (it is not closed, when you mouseover your mouse from it).
In this case you need to delete the mouse motion listeners from your menu items
JMenuItem jmi1 = new JMenuItem("JMenuItem1");
jmi1.removeMouseMotionListener(jmi1.getMouseMotionListeners()[0]);
jmi1.removeMouseListener(jmi1.getMouseListeners()[0]);
Related
I am developing a Java Swing application that will have several panels in it, one of which is NASA's WorldWind. In this WorldWind panel I need to have a right click menu for various actions. I was successful in adding the right click menu and all is good, until the user resizes the window.
I am not sure what about that action would be causing the right click menu to stop working. The mouse listener is working fine still, but the menu no longer shows up. None of the objects get garbage collected, nothing seems to change.
Here is a quick sample of how my code is set up:
class ClickListener extends MouseAdapter {
public void mouseClicked(MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e) == true) {
showRightClickMenu(e);
} else {
addPoint(e);
}
}
}
void showRightClickMenu(MouseEvent e) {
System.out.println("In Right Click");
if (menu == null) {
setupRightClickMenu();
}
menu.show(e.getComponent(), e.getX(), e.getY());
}
void setupRightClickMenu() {
menu = new JPopupMenu("RightClick");
JMenuItem button1 = new JMenuItem("Show Waypoint Table");
button1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
// ptTable.showTable();
}
});
menu.add(button1);
JMenuItem button2 = new JMenuItem("Clear Waypoints");
button2.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
ptLayer.removeAllRenderables();
}
});
menu.add(button2);
}
I can set the size to whatever I want at application launch and the right click works, but once I resize, it fails. Any ideas?
UPDATE
Actually after playing with it a bit more and talking with someone else I am working with, it looks like it may be drawing the menu behind the WorldWind canvas. I am not sure why this is, but I need to make sure it shows in front of it.
I tried changing the JPopupMenu property talked about here, but that does not seem to help.
I have a JPopupMenu which contains an inner JMenu and a separator with addSeparator(). Due to some odd handling, I've added a MouseListener to the JPopupMenu which makes it invisible on a mouseExited event. This works fine, except that when the mouse tries to cross over the separator, it's triggering the event (even though the JPopupMenu is the super component).
If I remove the addSeparator() line, it works as expected.
Is there any way to work around this? Or have I not set up the listener properly?
The code is like the following:
JPopupMenu popupMenu = new JPopupMenu();
JMenu innerMenu = new JMenu("Inner");
// ... add JMenuItems
popupMenu.add(innerMenu);
popupMenu.addSeparator();
popupMenu.add(new JMenuItem("Exit"));
popupMenu.addMouseListener(new MouseAdapter() {
#Override
public void mouseExited(MouseEvent e) {
popupMenu.setVisible(false);
}
});
Full Compilable Example
Simply comment and uncomment the popupMenu.addSeparator() line to notice the different behaviors
public class Test {
public static void main(String[] args) throws Exception {
if(!SystemTray.isSupported()) {
throw new UnsupportedOperationException("SystemTray is not supported.");
}
final TrayIcon trayIcon = new TrayIcon(ImageIO.read(new File("resources/icon.gif")));
final JPopupMenu popupMenu = new JPopupMenu();
JMenu intervalMenu = new JMenu("Interval");
ButtonGroup itemGroup = new ButtonGroup();
JRadioButtonMenuItem oneSecondMenuItem = new JRadioButtonMenuItem("1 sec");
itemGroup.add(oneSecondMenuItem);
JRadioButtonMenuItem twoSecondMenuItem = new JRadioButtonMenuItem("2 sec");
itemGroup.add(twoSecondMenuItem);
intervalMenu.add(oneSecondMenuItem);
intervalMenu.add(twoSecondMenuItem);
popupMenu.add(intervalMenu);
popupMenu.addSeparator();
JMenuItem exitMenuItem = new JMenuItem("Exit");
exitMenuItem.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
SystemTray.getSystemTray().remove(trayIcon);
System.exit(0);
}
});
popupMenu.add(exitMenuItem);
//Thanks to Artem Ananiev for this implementation idea
//https://weblogs.java.net/blog/ixmal/archive/2006/05/using_jpopupmen.html
trayIcon.addMouseListener(new MouseAdapter() {
#Override
public void mouseReleased(MouseEvent e) {
if(e.getButton() == MouseEvent.BUTTON3) {
popupMenu.setLocation(e.getX() - 40, e.getY() - 40);
popupMenu.setInvoker(popupMenu);
popupMenu.setVisible(true);
}
}
});
popupMenu.addMouseListener(new MouseAdapter() {
#Override
public void mouseExited(MouseEvent e) {
popupMenu.setVisible(false);
}
});
SystemTray.getSystemTray().add(trayIcon);
}
}
Wow, you are using a sytem tray icon. That information might have been important to know. That is why a SSCCE should be posted with EVERY question.
Anyway the following seems to work:
if (! popupMenu.contains( e.getPoint() ) )
popupMenu.setVisible(false);
Edit:
It looks like the problem is that the JSeparator does not listen for MouseEvents by default so all the mouse events are passed to its parent. So when you leave a JMenuItem, the mouseEntered() event is generated for the popup menu and then when you re-enter another JMenuItem the mouseExited() event is generated.
If you enable MouseEvents for the JSeparator then it look like the JPopupMenu doesn't get the event
//popupMenu.addSeparator();
popupMenu.add( new MySeparator() );
...
static class MySeparator extends JSeparator
{
public MySeparator( )
{
super( JSeparator.HORIZONTAL );
enableEvents(AWTEvent.MOUSE_EVENT_MASK);
}
/**
* Returns the name of the L&F class that renders this component.
*
* #return the string "PopupMenuSeparatorUI"
* #see JComponent#getUIClassID
* #see UIDefaults#getUI
*/
public String getUIClassID()
{
return "PopupMenuSeparatorUI";
}
}
I have menu in my application, and I want to set menu item normal state icon, and pressed state icon. Normal state icon is added, but when I press menu item, normal state icon is not changed by pressed state icon. What is problem here:
JMenu m=new JMenu(text);
m.setBackground(getTheme().colors.menuColor());
m.setOpaque(false);
m.setIcon(core.getIcon(text, "normal"));
m.setPressedIcon(core.getIcon("webmaps", "pressed"));
This issue has been seen before. The inherited setPressedIcon does not change the background Icon on the the JMenu (or indeed JMenuItem). You could use a MenuListener on the component as a workaround:
m.addMenuListener(new MenuListener() {
#Override
public void menuSelected(MenuEvent e) {
JMenu menu = (JMenu) e.getSource();
menu.setIcon(core.getIcon("webmaps", "pressed"));
}
#Override
public void menuDeselected(MenuEvent e) {
JMenu menu = (JMenu) e.getSource();
menu.setIcon(core.getIcon(text, "normal"));
}
#Override
public void menuCanceled(MenuEvent e) {
JMenu menu = (JMenu) e.getSource();
menu.setIcon(core.getIcon(text, "normal"));
}
});
I am trying to change the background of a JButton after it is clicked. Currently my buttons are located in a GridLayout (3x3) and look like this:
tiles.add(new JButton(new AbstractAction("") {
#Override
public void actionPerformed(ActionEvent e) {
this.setIcon("foo.png");
}
}));
This does not work. How do I manipulate the background of the image from within the actionperformed?
A JToggleButton might be ideal for this, as shown on Swing JToolbarButton pressing
Note that you would need to add some code to ensure a button was only clickable once.
Alternately you might use a standard JButton and call AbstractButton.setDisabledIcon(Icon). Disable the button when clicked, and it will flip to the alternate icon.
You can create your own listener that implements the MouseListener. This way, you can control when the background of the button changes (when the mouse is released, pressed, etc). Here is an example
//Add the listener to the button
myButton.addMouseListener(new customActionListener());
//Create the listener
class customActionListener implements MouseListener {
public void mouseExited(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
Icon icon = new ImageIcon("icon.gif");
myButton.setIcon(icon);
}
public void mouseClicked(MouseEvent e) {
}
}
At whatever point you want to set the background back to its default, use:
myButton.setIcon(new ImageIcon());
I am trying to write a Minesweeper clone in Java for fun. I have a grid of JButtons whose labels I will change to represent the danger count, flags, etc.
My problem is, I don't know how to get a right click on a JButton to depress the button. I've done the following:
button.addMouseListener(new MouseAdapter(){
public void mouseClicked(MouseEvent e){
boolean mine = field.isMine(x, y);
if (e.isPopupTrigger()) {
button.setText("F");
}
else {
if (mine) {
button.setText("X");
}
}
}
});
This doesn't seem to be working at all; the "F" is never shown, only the "X" part. But more importantly, this does nothing for depressing the button.
EDIT: Macs have popup trigger happen on mousePress, not mouseClick.
EDIT: Here's the solution I worked out based off of accepted answer:
button.addMouseListener(new MouseAdapter(){
boolean pressed;
#Override
public void mousePressed(MouseEvent e) {
button.getModel().setArmed(true);
button.getModel().setPressed(true);
pressed = true;
}
#Override
public void mouseReleased(MouseEvent e) {
//if(isRightButtonPressed) {underlyingButton.getModel().setPressed(true));
button.getModel().setArmed(false);
button.getModel().setPressed(false);
if (pressed) {
if (SwingUtilities.isRightMouseButton(e)) {
button.setText("F");
}
else {
button.setText("X");
}
}
pressed = false;
}
#Override
public void mouseExited(MouseEvent e) {
pressed = false;
}
#Override
public void mouseEntered(MouseEvent e) {
pressed = true;
}
});
add(button);
Minesweeper clone http://grab.by/1y9z
Button can't be pressed by right click. Add such a lines to you mouse listener
mousePressed:
if(isRightButtonPressed) {underlyingButton.getModel().setPressed(true));
mouseReleased:
if(needReset) {underlyingButton.getModel().setPressed(false));
or do there whatever want.
I wouldn't use isPopupTrigger but directly check for the right button:
button.addMouseListener(new MouseAdapter(){
public void mouseClicked(MouseEvent e){
boolean mine = field.isMine(x, y);
if (e.getButton() == MouseEvent.BUTTON2) {
button.setText("F");
}
...
Just a small addition: the simplest way to check for the right button is SwingUtilities.isRightMouseButton
As you have mentioned that checking for "mousePressed" solved your issue. And the Javadoc of isPopupTrigger would explain the need for this:
public boolean isPopupTrigger()
...
Note: Popup menus are triggered differently on different systems. Therefore, isPopupTrigger should be checked in both mousePressed and mouseReleased for proper cross-platform functionality.
Also see the section on The Mouse Listener API in the Java Swing tutorial.
MouseEvent has some properties
static int BUTTON1
static int BUTTON2
static int BUTTON3
among others. Check those when your event fires.
EDIT
public int getButton()
Returns which, if any, of the mouse buttons has changed state.
The button being visibly depressed on right click isn't part of the "normal" behavior of buttons. You may be able to fake it using JToggleButtons, or simply changing the button's background color and maybe border while the right mouse button is being held down.
If you are certain that the event is properly being triggered (debug FTW!) and that the button.setText("F") is happening, then perhaps you simply need to repaint.
Repaint the button.
http://java.sun.com/javase/6/docs/api/javax/swing/JComponent.html#repaint(java.awt.Rectangle)
This works for me fine on Mac:
import java.awt.event.*;
import javax.swing.*;
public class ButtonTest extends JFrame {
JButton button;
public ButtonTest() {
button = new JButton("W");
button.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (e.getButton() == 3) { // if right click
button.setText("F");
button.getModel().setPressed(false);
// button.setEnabled(true);
} else {
button.setText("X");
button.getModel().setPressed(true);
// button.setEnabled(false);
}
}
});
this.add(button);
this.setVisible(true);
}
public static void main(String[] args) {
new ButtonTest();
}
}
You might as well check for e.getButton() == 2 but I don't know when this one is triggered on Macs.