ActionListener & MouseListener confliction - java

I am trying to make my application minimize on taskbar and restore when I double click on the trayIcon. I also have a popup menu that has an item which restores the window when clicked.
trayIcon = new TrayIcon(image, "Anything", popup);
trayIcon.addActionListener(actionListener);
trayIcon.addMouseListener(mouseListener);
sysTray.add(trayIcon);
And here is the code of the actionListener and mouseListener:
private ActionListener actionListener = new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
if (e.getActionCommand().equals("Restore"))
{ // RIGHT CLICK -> RESTORE
// Do something
}
}
};
private MouseListener mouseListener = new MouseListener()
{
#Override
public void mouseClicked(MouseEvent e)
{
if (javax.swing.SwingUtilities.isLeftMouseButton(e) && e.getClickCount()>1)
{ // DOUBLE LEFT MOUSE CLICK
// Do something
}
}
// Rest of the code
}
The Restore option of the popup menu works fine, however when I am double-clicking on the trayIcon at the System Tray I get a Null Pointer Exception at line if(e.getActionCommand().equals("Restore"))
How can I eliminate this and if possible merge both listeners into one?

N.B. This answer is made from a conversation with the OP in the comments, and some of the solutions came from the OP
The TrayIcon does not populate the ActionCommand field when firing the event, hence the code dies with an NPE.
Since the tray icon only calls its ActionListener only when double clicked or in an analagous action (via keyboard), you can create a RestoreListener that does not check that condition at all, and is only used with the tray icon and the "Restore" menu item.
private ActionListener restoreListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// Do the actual restoration
}
};
And actually adding it to the items...
trayIcon = new TrayIcon(image, "Anything", popup);
trayIcon.addActionListener(restoreListener);
MenuItem restoreMenuItem = new MenuItem(...);
restoreMenuItem.addActionListener(restoreListener);
This seems to behave slightly differently from a MouseListener that it does not put the window on top, this can be remedied by calling toTop() on it.

Related

Java Swing JPopupMenu setVisible(false) doesn't work

I have a class MyPopupMenu that extends JPopupMenu. Inside this popup menu I add a JMenuItem with an ActionListener which calls a process that it takes some minutes to return. I would like to make the popup menu close right after this item is pressed. My method inside the MyPopupMenu class is this:
private JMenuItem newItem(){
JMenuItem item=new JMenuItem();
item.setText("One");
item.setToolTipText("One");
ActionListener mylistener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
MyPopupMenu.this.setVisible(false);
Class1 class1=new Class1(file);
try {
class1.start();
} catch (IOException e) {
e.printStackTrace();
}
}
};
item.addActionListener(mylistener);
return item;
}
This doesn't work. After I press the item "One" the process starts but the popup menu remains open until the process returns (for some minutes). Is it possible to make the the popup menu disappear but the process continue running?
You are performing an action on the Swing main thread. This blocks your Gui from updating. You should move the starting of you class into another Thread.
For more info see Here

Right click popup menu in Processing

I am a beginner in both Java and Processing. I am trying to create a right click popup menu that will be displayed inside a processing jPanel. Part of the code I m using is displayed bellow, belongs to the class that extends PAplet. Initially, popup menu is displayed properly when right mouse buttonis pressed, but after using some other elements of the GUI, it doesnt appear on the screen anymore.
How to fix this? Shall I create the popup menu inside steup() method? Or elsewhere?
Thanks.
private JPopupMenu menu = new JPopupMenu();
private JMenuItem edgeFrom = new JMenuItem("Edge From");
private JMenuItem edgeTo = new JMenuItem("Edge To");
#Override
public void setup() {
size(desiredheight,desiredwidth);
background(255);
PopupActionHandler handler = new PopupActionHandler();
edgeFrom.addActionListener(handler);
edgeTo.addActionListener(handler);
menu.add(edgeFrom);
menu.add(edgeTo);
addMouseListener(new MouseAdapter() {
public void mouseReleased(MouseEvent evt) {
if (evt.isPopupTrigger()) {
menu.show(evt.getComponent(), evt.getX(), evt.getY());
}
}
});
}
Use the below reference link for JPopupMenu
http://www.java2s.com/Code/Java/Swing-JFC/AsimpleexampleofJPopupMenu.htm

jframe actionperformed jframe

I currently have a button that, when clicked, performs a method that creates a jframe with a panel that loads multiple images. If this button is clicked multiple times, the images keep adding onto the prexisting images loaded onto the jframe. What code should I use so that if the button is clicked after the jframe and elements have been loaded, after clicking once, nothing else will be added.
Many thanks
What about:
public void actionPerformed(ActionEvent evt) {
button.setEnabled(false);
// the other code that creates imgFrame
imgFrame.addWindowListener(new WindowAdapter() {
#Override public void windowClosing(WindowEvent evt) {
button.setEnabled(true);
}});
imgFrame.setVisible(true);
}
don't create lots of JFrames on the runtime, because these Object never gone from Used JVM Memory untill current JVM instance exist
you have look at CardLayout that very confortly to solve your issues with multiple of views (in this case in one JFrame)
put Images as Icon/ImageIcon to the JLabel
Disable the button when frame show up, and when the frame close, enable the button.
public void actionPerformed(ActionEvent evt) {
final JButton finalButton = button;
button.setEnabled(false);
JFrame frame = new JFrame()
{
protected void processWindowEvent(WindowEvent e)
{
if (e.getID() == WindowEvent.WINDOW_CLOSING)
{
finalButton.setEnabled(true);
}
super.processWindowEvent(e);
}
};
frame.setVisible(true);
}
I suggest a boolean that is false when the program starts, and then when the button is clicked, it tests if the boolean is false. If it is false, then create the stuff that you want, and then make it true. If it is true, do nothing, or alert the user not to click the button more that once, or something of that matter
boolean isAlreadyCreated = false;
yourButton.addActionListener(new ActionListener()
{
if(!isAlreadyCreated)
{
//CREATE YOUR NEW FRAME
isAlreadyCreated = true;
}
});

How to wait for radio button to get selected in lost focus event

I have a Swing program that does a search based on the contents of some text fields and settings of a pair of radio buttons (in a button group). The program will automatically search when certain of the text fields lose focus. The problem comes in when the lose focus event is triggered by a click on one of the radio buttons. The lost focus event on the text field is getting processed before the radio button isSelected() values have changed, so the search is done with the "wrong" (i.e. old) parameters, instead of the parameters based on the new setting of the radio buttons.
I tried invoking the search using my own invokeWhenIdle method (shown below) to run the search after the event queue had settled down, but it still is using the old setting of the radio buttons.
My only working solution is to delay for 250 milliseconds in the lost focus event before running the search, so that the radio buttons have time to change. This works, but it makes the UI seem sluggish.
Any better ideas?
public static void invokeWhenIdle(final int a_max_retry, final Runnable a_runnable) {
if (a_max_retry <= 0) {
throw new IllegalStateException("invokeWhenIdle: Could not run " + a_runnable);
}
// get the next event on the queue
EventQueue l_queue = Toolkit.getDefaultToolkit().getSystemEventQueue();
AWTEvent l_evt = l_queue.peekEvent();
if (l_evt == null) {
// nothing left on the queue (but us), we can do it
SwingUtilities.invokeLater(a_runnable);
} else {
// still something in the queue, try again
SwingUtilities.invokeLater(new Runnable() {
public void run() {
invokeWhenIdle(a_max_retry - 1, a_runnable);
}
});
}
}
Not an answer, but an explanation about what is happening. Maybe it will spark an idea...
The problem is that a mousePressed arms the button model and the mouseReleased actually changes the selected value of the model.
When you execute the FocusListener code the radio button the model is in an undefined state. Even if you add the FocusListener code to the end of the EDT by using invokeLater the code will still execute before the mouseReleased event is generated.
The following shows how you might code the listener to handle this. It assumes the state of the button is about to change:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class FocusSSCCE extends JPanel
{
public FocusSSCCE()
{
final JRadioButton radio = new JRadioButton("Radio");
add( radio );
radio.setMnemonic('R');
JTextField textField = new JTextField(10);
add( textField );
JButton button = new JButton("Button");
add( button );
textField.addFocusListener( new FocusAdapter()
{
public void focusLost(FocusEvent e)
{
boolean isSelected = radio.isSelected();
// Assumes selected state will change
if (radio.getModel().isArmed())
isSelected = !isSelected;
System.out.println( isSelected );
}
});
}
private static void createAndShowUI()
{
JFrame frame = new JFrame("FocusSSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new FocusSSCCE() );
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
However, even this approach can't be guaranteed to work. If for some reason the user generates the mousePressed event on the radio button and them moves the mouse away from the radio button before releasing the mouse, then the selected state of the radio button is not changed.
Similiarly, even your original implementation to sleep for 250ms can not be guaranteed to work because the user could theoretically hold down the mouse for more than 250ms which would also generate the wrong value.
My workaround for this was to make the radio buttons non focusable
I can't think of any better approach.
Edit:
I just thought of a wild solution.
textField.addFocusListener( new FocusAdapter()
{
public void focusLost(FocusEvent e)
{
if (e.getOppositeComponent() instanceof JRadioButton)
{
final JRadioButton radio = (JRadioButton)e.getOppositeComponent();
MouseListener ml = new MouseAdapter()
{
public void mouseReleased(MouseEvent e)
{
System.out.println( radio.isSelected() );
radio.removeMouseListener(this);
}
};
radio.addMouseListener( ml );
}
else
System.out.println( radio.isSelected() );
}
});
Basically your processing code won't execute until the mouse has been released when you click on the radio button.

How can I create a "Drop-Down" menu in a Java Swing toolbar?

I've created a drop-down menu on my Swing JToolBar. But it doesn't create behave the way I want. I'm aiming for it to work like Firefox's "Smart Bookmarks" button.
It disappears when the user selects a menu item: CORRECT!
It disappears when the user presses ESC: CORRECT!
It disappears when the user clicks somewhere in the main frame outside of the menu: CORRECT!
But it doesn't disappear when the user clicks a second time on the button that shows the drop-down menu: INCORRECT... :-(
My question is how can I add this behaviour, that it does disappear when the clicks on the button that shows the menu a second time.
Here's my current code, from Java 6 on the Mac:
import javax.swing.*;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
public class ScratchSpace {
public static void main(String[] arguments) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("Toolbar with Popup Menu demo");
final JToolBar toolBar = new JToolBar();
toolBar.add(createMoreButton());
final JPanel panel = new JPanel(new BorderLayout());
panel.add(toolBar, BorderLayout.NORTH);
panel.setPreferredSize(new Dimension(600, 400));
frame.getContentPane().add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
private static AbstractButton createMoreButton() {
final JToggleButton moreButton = new JToggleButton("More...");
moreButton.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
createAndShowMenu((JComponent) e.getSource(), moreButton);
}
}
});
moreButton.setFocusable(false);
moreButton.setHorizontalTextPosition(SwingConstants.LEADING);
return moreButton;
}
private static void createAndShowMenu(final JComponent component, final AbstractButton moreButton) {
JPopupMenu menu = new JPopupMenu();
menu.add(new JMenuItem("Black"));
menu.add(new JMenuItem("Red"));
menu.addPopupMenuListener(new PopupMenuListener() {
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
}
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
moreButton.setSelected(false);
}
public void popupMenuCanceled(PopupMenuEvent e) {
moreButton.setSelected(false);
}
});
menu.show(component, 0, component.getHeight());
}
}
Well, here is a potential solution that is not without it's drawbacks. Only you can decide if this is acceptable for your application. The issue is that the popup closing occurs before other mouse-handling events are fired so clicking on your More.. button again causes the popup to hide, thus resetting the buttons state to deselected BEFORE the button even gets told it was pressed.
The easy workaround is to add the following call within your main program:
UIManager.put("PopupMenu.consumeEventOnClose", Boolean.TRUE);
The result of this is that whenever a popup menu is closed because of a mouse-pressed event, that mouse event will be consumed at the time the menu is closed and won't be passed on to any other components under the mouse. If you can live with limitation, this is an easy solution.
What's happening is that when you click off the menu, it cancels the popup menu, so you deselect the button, but the next immediate event is clicking the button, and now its deselected so it shows the menu again.
I don't have the exact solution yet, but give me a little bit ...
I don't use Firefox so I don't know what the Smart Bookmarks button looks like, but maybe use a JMenu as the "button". You could try using the Border of a JButton to make it look more like a button.
Well, the listener on the button reacts only when it is pushed down, because you listen for ItemEvent.SELECTED events only. How about adding another if clause to listen for ItemEvent.DESELECTED events here:
moreButton.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
createAndShowMenu((JComponent) e.getSource(), moreButton);
}
}
});
You could either store a reference to the menu somewhere, or you could make the menu itself add another listener to the button. The latter solution could be more straightforward, since you already seem to send a button reference to the menu.

Categories