Java Swing, restore from system tray not working in Linux - java

I am creating an application in Java and I would like that when you minimize to an icon, the application will have to "hide" in the system tray.
The code I use is this: (the significant part of the code)
myFrame = new JFrame();
myFrame.addWindowListener(new WindowAdapter() {
#Override
public void windowIconified(WindowEvent e) {
PutTray();
}
#Override
public void windowDeiconified(WindowEvent e) {
System.out.println("Deiconified");
}
});
This is a "PutTray" function:
private void PutTray()
{
try
{
tray.add(trayIcon); // Initialized elsewhere
myFrame.setVisible(false);
} catch (AWTException e) {
System.err.println(e);
}
}
To restore (via option in the pop-up menu when you press the icon minimized):
MenuItem show = new MenuItem("Show");
show.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
myFrame.setVisible(true);
myFrame.setState(JFrame.NORMAL);
tray.remove(trayIcon);
}
});
The code works perfectly on Windows 8, but it does not work on Linux (Kali Linux and even Ubuntu).
Why Windows yes and Linux no?
EDIT:
On Linux, after you press the command to show the application, it appears for a very small moment, and then minimizes again. Basically is triggered the event "windowDeiconified" and immediately after the event "windowIconified" without taking the time to do something else and then the application is shown in the system tray.

As Dan Getz suggests, I also thought the order of setVisible and setState should be inverted since the javadoc for setState says:
If the frame is not visible on the
* screen, the events may or may not be
* generated.
but this didn't help.
The one thing that did help though was replacing setVisible(false) with dispose(). They are similar in that you can use setVisible(true) to reopen a disposed window. You can read more about it here: JDialog setVisible(false) vs dispose()
I'll try to find an explanation and come back with it :)
SSCCE to simulate OP problem:
public class Test {
private JFrame myFrame;
public Test() {
myFrame = new JFrame();
myFrame.setVisible(true);
myFrame.setSize(300, 300);
myFrame.addWindowListener(new WindowAdapter() {
#Override
public void windowIconified(WindowEvent e) {
PutTray();
}
});
}
private void PutTray() {
myFrame.setVisible(false); //replace with dispose(); and it's ok
Timer t = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent actionEvent) {
myFrame.setVisible(true);
}
});
t.setRepeats(false);
t.start();
}
public static void main(String[] args) {
new Test();
}
}

I think you are getting it wrong!
Maybe you are confused about deiconified and visibility
windowIconified()
will be called when we click minimize button
and
windowDeiconified()
is called when we restore it from taskbar and not system tray!
In order to restore from system tray you need to use this
trayIcon.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
window.setVisible(true);
}
});
Basically i don't think the difference between dispose() & setVisible() will bother you in this specific problem
Still, my recommendation is to use setVisible() here

Related

how to close window instead of a JFrame

Somehow I cannot access & close a JFrame with.dispose(), and it gives me a nullPointerException. Neither do I want to do a System.exit(0). How do I access the JFrame directly, is there a workaround to close the JFrame?
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
try
{
frame = new ScannerUI();
frame.setVisible(true);
}
catch (Exception e)
{
e.printStackTrace();
}
}
});
}
... (further down, and I cannot access the JFrame already, gives me a nullpointerexception)
btnBack.setBounds(400, 270, 80, 40);
panel.add(btnBack);
btnBack.setText ("BACK");
btnBack.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
//ScannerUI.DISPOSE_ON_CLOSE();
//frame.dispose();
//this.dispose();
//frame.setVisible(false);
//System.out.println ("dsakjf;dsalkhfsa;lklf");
//System.exit(0);
//JFrame test = ScannerUI.frame;
//test.dispose();
// p = false;
System.out.println ("asdfasfas");
System.exit(frame.dispose());
}
});
You could use the SwingUtilities method, getWindowAncestor, to help you get the window that holds the button and then call dispose on it:
btnBack.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
AbstractButton button = (AbstractButton) e.getSource();
Window window = SwingUtilities.getWindowAncestor(button);
window.dispose();
}
});
Another option is to get the enclosing object of the current class (if it is the JFrame). You can get this from within your anonymous inner class by using the class name, a period, followed by this, or for you: ScannerUI.this:
btnBack.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
ScannerUI.this.dispose();
}
});
You can also check out Closing an Application and use the ExitAction. This is a more generic solution that will simulate a user clicking the "X" on the window. In this case any WindowsListeners you have added to the frame will be invoked first before the window is closed.
May not be applicable in this case, but just something to think about.

How to trap the Window minimizing event?

I want to create a JFrame instance and on the click of its minimize button, I would like to hide it to the System Tray which is usually the taskbar of windows.
I'd come to know that by using SystemTray class in java.awt package I can do so but neither I'm getting any tutorial on it nor any working program example.
I'd asked this question here to either get the link to tutorial site for SystemTray class or if any body knows how to trap the window minimizing event, a working example.
The WindowListener interface and JFrame's addWindowListener() method should help you determine when the frame has been minimised.
This will trap the window minimized event and will create a tray icon. It will also remove the window from the taskbar and it will add a listener on the tray icon so that a mouseclick would restore the window. The code is a bit scrappy but should be good enough for your learning purposes:
public class Qwe extends JFrame {
public static void main(String[] args) {
final Qwe qwe = new Qwe();
qwe.addWindowStateListener(new WindowStateListener() {
public void windowStateChanged(WindowEvent e) {
if (e.getNewState() == ICONIFIED) {
try {
final TrayIcon trayIcon = new TrayIcon(new ImageIcon("/usr/share/icons/gnome/16x16/emotes/face-plain.png").getImage());
trayIcon.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
qwe.setVisible(true);
SystemTray.getSystemTray().remove(trayIcon);
}
});
SystemTray.getSystemTray().add(trayIcon);
qwe.setVisible(false);
} catch (AWTException e1) {
e1.printStackTrace();
}
}
}
});
qwe.setSize(200, 200);
qwe.setVisible(true);
}
}
best way would be create follows
1) SystemTray
2) add JPopopMenu to the SystemTray's Icon
3) set DefaultCloseOperation for TopLevelContainer (in your case JFrame)
by using WindowListener setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
in other cases always works setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
notice don't forget declare System.exit(1) to the SystemTray's JpopupMenu, from JMenuItem or another Action/Event, because in this form currenet JVM never gone from Native OS until PC power-off or restart
private void windowStateChanged(java.awt.event.WindowEvent evt) {
// Use getExtendedstate here.
}
WindowStateListener docs
Frame.getExtendedState() docs
frame.addWindowListener(new WindowAdapter() {#Override
public void windowIconified(WindowEvent e) {}
});

Unable to receive grab event by using GRAB_EVENT_MASK?

I added an AWTEventListener to process grab event. So this listener just use sun.awt.SunToolkit.GRAB_EVENT_MASK
mark.
But This listener can not capture UngrabEvent. The tricky thing is, when a JComboBox popuped its menulist, it can capture this event.
I use the following code for testing.
Start the program, click on the empty area of the frame, click on the frame title. Then there should be an UngrabEvent. But the listener does not capture it.
Start the program, click on the combobox and make its menulist popuped. click on the
frame title. Then there should be an UngrabEvent. And the listener captures it.
It is very strange...Is there any relationship between UngrabEvent and JComboBox?
public class ComboboxLearn {
public static void main(String[] args) {
// TODO Auto-generated method stub
Toolkit toolkit = Toolkit.getDefaultToolkit();
toolkit.addAWTEventListener(new AWTEventListener() {
#Override
public void eventDispatched(AWTEvent event) {
System.out.println(event);
}
}, sun.awt.SunToolkit.GRAB_EVENT_MASK);
JComboBox box = new JComboBox(new Object[] { "AAA", "BBB", "CCC" });
box.addPopupMenuListener(new PopupMenuListener() {
#Override
public void popupMenuCanceled(PopupMenuEvent e) {
System.out.println(e);
}
#Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
System.out.println(e);// Set a breakpoint here
}
#Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
System.out.println(e);
}
});
JFrame f = new JFrame();
f.getContentPane().setLayout(new FlowLayout());
f.getContentPane().add(box);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(new Dimension(100, 100));
f.setVisible(true);
}
}
I sense you're experimenting; but generally, you shouldn't rely on Sun/Oracle's undocumented APIs.
I want a popup that will hide when the
mouse is pressed outside the popup but
not hide when the mouse is pressed on the popup.
Why not bring up a JDialog when you see isPopupTrigger() and hide it when you see it deactivating, as another window activates? The notion is discussed here.
Although trashgod's reasoning is understandable, it doesn't answer the question: What you are actually trying to do won't work because for the grab even to fire, you need to have a window grabbed: ((SunToolkit)Toolkit.getDefaultToolkit()).grab(someWindow);.
You could change your code as follows
class ComboboxLearn {
public static void main(String[] args) {
// TODO Auto-generated method stub
Toolkit toolkit = Toolkit.getDefaultToolkit();
toolkit.addAWTEventListener(new AWTEventListener() {
#Override
public void eventDispatched(AWTEvent event) {
System.out.println(event);
}
}, sun.awt.SunToolkit.GRAB_EVENT_MASK);
JComboBox box = new JComboBox(new Object[] { "AAA", "BBB", "CCC" });
JFrame f = new JFrame();
box.addPopupMenuListener(new PopupMenuListener() {
#Override
public void popupMenuCanceled(PopupMenuEvent e) {
System.out.println(e);
}
#Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
System.out.println(e);// Set a breakpoint here
//UNGRABBING WINDOW
((SunToolkit) toolkit).ungrab(f);
}
#Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
System.out.println(e);
//GRABBING WINDOW
((SunToolkit) toolkit).grab(f);
}
});
f.getContentPane().setLayout(new FlowLayout());
f.getContentPane().add(box);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(new Dimension(100, 100));
f.setVisible(true);
}
}
Then you will get your grab events but at the level you are working on you don't need them.
Then when would I need them?
Assuming you run into this nasty bug which although closed, I could still reproduce it, you need to implement your own popup mechanism. You did everything fine you attached your AWT listeners to close the popup whenever there is a click outside it but something is missing. You click everywhere and the popup disappears except on the window captions and outside your application! Shoot you think! How does JPopupmenu do it. And after you spend some time reading jdk code and trying various stuff, you realize that it's this undocumented event that does the trick.
I don't know the internals of this grab() method and I don't have time to investigate so there might be side effects not very obvious. Call it on your own risk.

How do I close a JDialog and have the Window Event Listeners be notified?

Is there a way to close a JDialog through code such that the Window event listeners will still be notified? I've tried just setting visible to false and disposing, but neither seem to do it.
Closing a window (with dispose()) and hiding it (with setVisible(false)) are different operations, and produce different events -- and closing it from the operating system is yet another different operation that produces yet a different event.
All three will produce windowDeactivated to tell you the window's lost focus, but dispose() will then produce windowClosed, while closing from the OS will first produce windowClosing. If you want to handle both of these the same way, you can set the window to be disposed when closed:
window.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
In general, setVisible(false) implies that you might want to use the window again, so it doesn't post any window events (apart from windowDeactivated). If you want to detect the hiding of a window, you need to use a ComponentListener;
window.addComponentListener(new ComponentAdapter() {
#Override
public void componentHidden(ComponentEvent e) {
System.out.println("componentHidden()");
}
})
Note though that this will pretty much only work for explicit setVisible() calls. If you need to detect hiding more generally, you can use a HierarchyListener, but it's probably more trouble than it's worth.
window.addHierarchyListener(new HierarchyListener() {
#Override
public void hierarchyChanged(HierarchyEvent e) {
System.out.println("valid: " + window.isValid());
System.out.println("showing: " + window.isShowing());
}
});
Note that when you dispose a window you'll get a couple of HierarchyEvents, first for hiding and then for invalidation, but when you hide it with setVisible() it's still valid, so you won't get the invalidation.
I don't seem to have your problem. When I use the code below windowDeactivated() is called for either setVisible( false ) or dispose() and windowClosed() is also called for dispose().
ClosingDialog.java:
public class ClosingDialog extends JDialog {
public ClosingDialog(Frame owner, String title, boolean modal) {
super(owner, title, modal);
JPanel contentPanel = (JPanel) this.getContentPane();
JButton setVisButton = new JButton("setVisible( false )");
setVisButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ClosingDialog.this.setVisible(false);
}
});
JButton disposeButton = new JButton("dispose()");
disposeButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ClosingDialog.this.dispose();
}
});
contentPanel.setLayout(new FlowLayout());
contentPanel.add(setVisButton);
contentPanel.add(disposeButton);
this.addWindowListener(new WindowListener() {
public void windowActivated(WindowEvent e) {
System.out.println("windowActivated");
}
public void windowClosed(WindowEvent e) {
System.out.println("windowClosed");
}
public void windowClosing(WindowEvent e) {
System.out.println("windowClosing");
}
public void windowDeactivated(WindowEvent e) {
System.out.println("windowDeactivated");
}
public void windowDeiconified(WindowEvent e) {
System.out.println("windowDeiconified");
}
public void windowIconified(WindowEvent e) {
System.out.println("windowIconified");
}
public void windowOpened(WindowEvent e) {
System.out.println("windowOpened");
}
});
this.setSize(300, 300);
}
}
Dispatch a windowClosing event to the Window. Check out the ExitAction example from the Closing an Application entry.
Untested suggestion:
Have you tried getWindowListeners() and then iterating around to fire windowClosed() to each of the WindowListeners?
EDIT: the above suggestion is wrong. Keeping it for posterity.
I'm afraid calling dialog.dispose() works fine for me in my simple example.
I wanted to fire a windowClosing event from the code (just as if the user clicked the X), because I have an extra close button in the JDialog and want the same WindowListener (that I implemented using a WindowAdapter) to be run when the X is clicked and when the button is clicked. Running dispose() only fires windowClosed, not windowClosing, and I want a message to appear before the window is closed, for confirmation. I also didn't manage to fire windowClosing via JDialog's method processWindowEvent since it is protected.
Here is how I got it working though:
WindowAdapter adapter = (WindowAdapter)jdialog.getWindowListeners()[0];
adapter.windowClosing(new WindowEvent((Window)jdialog, WindowEvent.WINDOW_CLOSING));
Hope that helps someone.

Why wont my JFrame hide?

I'm in the process of creating a GUI in Netbeans 6.1 for my senior design project but i've run into an annoying snag. Temporary Windows like my login PopUp and others wont disappear when i tell it. I've been researching how to solve this for about 2 months on an off. I've even mad a separate thread for my Pop Up but it still wont work....the only way it will disappear if i literally dont mess with any of the other GUI components....my sample code should help describe my anger...dont mind the shadow code, it was for testing purposes, which obviously didnt help.
//This method is called once a user presses the "first" login button on the main GUI
public synchronized void loginPopUpThread() {
doHelloWorld = new Thread(){
#Override
public synchronized void run()
{
try
{
loginPopUpFrame.pack();
loginPopUpFrame.setVisible(true);
System.out.println("waitin");
doHelloWorld.wait();
System.out.println("Not Sleepin..");
loginPopUpFrame.pack();
loginPopUpFrame.setVisible(false);
}
catch (InterruptedException e)
{
}
}
};
doHelloWorld.start();
//This is called when the "second" loginB is pressed and the password is correct...
public synchronized void notifyPopUp() {
synchronized(doHelloWorld) {
doHelloWorld.notifyAll();
System.out.println("Notified");
}
}
I've also tried Swing Utilities but maybe i implemented it wrong as it's my first time using them. It essentially does the same thing as the code above except the window freezes when it gets to wait, which the above code doesnt do:
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public synchronized void run() {
try
{
loginPopUpFrame.pack();
loginPopUpFrame.setVisible(true);
System.out.println("waitin");
wait();
System.out.println("Not Sleepin.");
loginPopUpFrame.pack();
loginPopUpFrame.setVisible(false);
}
catch (InterruptedException e)
{
}
}
});
PLEASE HELP ME!!!
Rules of thumb:
Don't manipulate GUI components in arbitrary threads; always arrange to manipulate them in the event thread
Never wait or sleep inside the event thread (so, never inside code sent to invokeLater())
So the answer to how you solve this problem is "some other way"...
Standing back from the problem a bit, what is it you're actually trying to do? If you just want a login dialog to wait for the user to enter user name and password, is there a reason not to just use a modal JDialog (after all, that's what it's there for...).
If you really do want some arbitrary thread to wait for a signal to close the window/manipulate the GUI, then you need to do the waiting in the other thread, and then make that thread call SwingUtilities.invokeLater() with the actual GUI manipulation code.
P.S. There are actually some GUI manipulation methods that it is safe to call from other threads, e.g. calls that are "just setting a label" are often safe. But which calls are safe isn't terribly well-defined, so it's best just to avoid the issue in practice.
The Swing components should only be manipulated by the swing event dispatch thread.
class SwingUtilites has methods to submit tasks to the dispatch thread.
It is difficult to diagnose your problem. I'm not sure what you're trying to do with the wait methods, but I recommend leaving wait/notify alone.
This code has two frames - when you create a second frame, the first is hidden until you close it.
public class SwapFrames {
private JFrame frame;
private JFrame createMainFrame() {
JButton openOtherFrameButton = new JButton(
"Show other frame");
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container contentPane = frame.getContentPane();
contentPane.setLayout(new FlowLayout());
contentPane.add(openOtherFrameButton);
frame.pack();
openOtherFrameButton
.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
onClickOpenOtherFrame();
}
});
return frame;
}
private void onClickOpenOtherFrame() {
frame.setVisible(false);
JFrame otherFrame = new JFrame();
otherFrame
.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
otherFrame.setContentPane(new JLabel(
"Close this to make other frame reappear."));
otherFrame.pack();
otherFrame.setVisible(true);
otherFrame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosed(WindowEvent e) {
frame.setVisible(true);
}
});
}
public static void main(String[] args) {
JFrame frame = new SwapFrames().createMainFrame();
frame.setVisible(true);
}
}
Because I don't see any evidence of them in your code, I'm going to suggest you read up on using event listeners rather than trying to "wait" for code to finish.
It isn't entirely clear what you're trying to achieve, but you might be better off with a modal dialog:
public class DialogDemo {
public JFrame createApplicationFrame() {
JButton openDialogButton = new JButton("Open Dialog");
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container container = frame.getContentPane();
container.setLayout(new FlowLayout());
container.add(openDialogButton);
frame.pack();
openDialogButton
.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
onOpenDialog(frame);
}
});
return frame;
}
private void onOpenDialog(JFrame frame) {
JDialog dialog = createDialog(frame);
dialog.setVisible(true);
}
private JDialog createDialog(JFrame parent) {
JButton closeDialogButton = new JButton("Close");
boolean modal = true;
final JDialog dialog = new JDialog(parent, modal);
dialog
.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
Container container = dialog.getContentPane();
container.add(closeDialogButton);
dialog.pack();
dialog.setLocationRelativeTo(parent);
closeDialogButton
.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
dialog.setVisible(false);
}
});
return dialog;
}
public static void main(String[] args) {
new DialogDemo().createApplicationFrame().setVisible(
true);
}
}
How about doing simply:
//This method is called once a user presses the "first" login button on the main GUI
public void loginPopUpThread() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
loginPopUpFrame.pack();
loginPopUpFrame.setVisible(true);
}
};
}
//This is called when the "second" loginB is pressed and the password is correct...
public void notifyPopUp() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
loginPopUpFrame.setVisible(false);
}
};
}
What you really want to be using is a modal JDialog.
Note, bits of this are left out. It's your homework/project.
public void actionPerformed(ActionEvent e)
{
// User clicked the login button
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
LoginDialog ld = new LoginDialog();
// Will block
ld.setVisible(true);
}
});
}
public class LoginDialog extends JDialog
{
public LoginDialog()
{
super((Frame)null, "Login Dialog", true);
// create buttons/labels/components, add listeners, etc
}
public void actionPerformed(ActionEvent e)
{
// user probably clicked login
// valid their info
if(validUser)
{
// This will release the modality of the JDialog and free up the rest of the app
setVisible(false);
dispose();
}
else
{
// bad user ! scold them angrily, a frowny face will do
}
}
}

Categories

Resources