There is a java swing application that I need to automate one of the functions of. It's quite simple - the user clicks a button in the swing application and starts an action.
I made a small java application that includes the java swing application as a .jar and calls the action behind the button (read).
The problem is - in case of an exception, the swing .jar shows JOptionPane, which halts the automated execution. Is it possible to somehow override this behavior without altering the original jar?
Application structure:
Main.java
import com.swingapp.ui
public static void main(String[] args){
Swingapp.read();
}
Then the read() function in the Swingapp library:
public void read(){
try{
//do a bunch of stuff...
} catch (Exception ex){
JOptionPane.showMessageDialog(null, ex.getMessage()); // In case of an exception, the swing application will show a message dialog. This halts the automated execution of my java task, I'd like to just skip this
}
When exception happens in above application, user is expected to click "OK". But running this as automated task, nobody there to click okay
Since a JOptionPane gains focus as soon as it opens (I think the most right button gets the focus, but it does not matter in your case) you can do the following:
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
#Override
public void eventDispatched(AWTEvent arg0) {
Component c = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
while(c != null) {
if (c instanceof JOptionPane) {
try {
new Robot().keyPress(KeyEvent.VK_ENTER);
} catch (AWTException e) {
e.printStackTrace();
break;
}
}
c = c.getParent();
}
}
}, AWTEvent.FOCUS_EVENT_MASK);
It will traverse up to see if anything in the current hierarchy is an instance of JOptionPane. If so -> simulate that the user pressed Enter (Return) which will close the dialog even if the focus is in an input field.
I have following solution for you. You need to registrate a listener to monitor all window events. Use Toolkit.getDefaultToolkit().addAWTEventListener(). If you get a window opened event, try to check whether the window is a JDialog and whether the dialog's contentPane contains an instance of JOptionPane. If yes you need traverse the component tree, find the first button and click it.
Related
I'm making a small program in Java using the Robot class. The program takes over the mouse. while in the course of debugging if it starts acting in a way that I don't want it's hard to quit the program, since I can't move the mouse over to the terminate button in eclipse, and I can't use hotkeys to hit it because the mouse is constant clicking in another window, giving that window focus instead.
What I'd like to do is just hook up a keylistener so that when I hit q I can quit the program, but the only way I know how to do this involves making a window, and that window needs focus to capture the input. Is there a way to listen for keyboard or mouse input from anywhere, regardless of what has focus?
There is a library that does the hard work for you:
https://github.com/kwhat/jnativehook
This is not a trivial problem and Java doesn't give you a way to do it elegantly. You can use a solution like banjollity suggested but even that won't work all the time if your errant mouse clicks open another fullsized window currently open in your taskbar for example.
The fact is, Java by default gives developers very little control over the OS. This is due to 2 main reasons: security (as citied by java documentation) and the fact that different operating systems handle events completely differently and making one unified model to represent all of these would probably not make a whole lot of sense.
So to answer your question, I imagine what you want is some kind of behaviour for your program where it listens for keypresses globally, not just in your application. Something like this will require that you access the functionality offered by your OS of choice, and to access it in Java you are going to need to do it through a Java Native Interface (JNI) layer.
So what you want to do is:
Implement a program in C that will listen for global keypresses on your OS, if this OS is Windows than look for documentation on windows hooks which is well docuemented by Microsoft and MSDN on the web and other places. If your OS is Linux or Mac OS X then you will need to listen for global keypresses using the X11 development libraries. This can be done on an ubunutu linux distro according to a Howto that I wrote at http://ubuntuforums.org/showthread.php?t=864566
Hook up your C code to your Java code through JNI. This step is actually the easier step. Follow the procedure that I use in my tutorial at http://ubuntuforums.org/showthread.php?t=864566 under both windows and linux as the procedure for hooking up your C code to your Java code will be identical on both OSes.
The important thing to remember is that its much easier to get your JNI code working if you first code and debug your C/C++ code and make sure that it is working. Then integrating it with Java is easy.
Had same problem. In my case, robot just controlled a single Windows App, that was maximized. I placed these lines at top of main loop driving the robot:
Color iconCenterColor = new Color(255,0,0); // if program icon is red
if (iconCenterColor.equals(robot.getPixelColor(10,15)))
throw new IllegalStateException("robot not interacting with the right app.");
To cancel the robot, just alt-tab to another app. Works great for a simple one app driving robot.
Start the program from a command line in a terminal and use Ctrl-C to terminate it.
(As mentioned by #MasterID and shown on JNativeHook's documentation for native keyboard input detection {main GitHub project here}),
This code should be enough to listen to any key without app focus (press and/or release):
>>Remember to add the jnativehook library in your project to be able to use all its utilities.<<
public class yourClass implements NativeKeyListener {//<-- Remember to add the jnativehook library
public void nativeKeyPressed(NativeKeyEvent e) {
System.out.println("Key Pressed: " + NativeKeyEvent.getKeyText(e.getKeyCode()));
}
public void nativeKeyReleased(NativeKeyEvent e) {
System.out.println("Key Released: " + NativeKeyEvent.getKeyText(e.getKeyCode()));
}
public void nativeKeyTyped(NativeKeyEvent e) {
System.out.println("Key Typed: " + NativeKeyEvent.getKeyText(e.getKeyCode()));
}
public static void main(String args[]){
//Just put this into your main:
try {
GlobalScreen.registerNativeHook();
}
catch (NativeHookException ex) {
System.err.println("There was a problem registering the native hook.");
System.err.println(ex.getMessage());
System.exit(1);
}
GlobalScreen.addNativeKeyListener(new yourClass());
//Remember to include this^ ^- Your class
}
}
For this particular problem, use the nativeKeyPressed method like this:
public void nativeKeyPressed(NativeKeyEvent e) {
System.out.println("Key Pressed: " + NativeKeyEvent.getKeyText(e.getKeyCode()));
if (e.getKeyCode() == NativeKeyEvent.VC_Q){
System.exit(1);
}
}
Note that JNativeHook by default shows a lot of stuff in your console that you might not want, to change that, just add this right before the try-catch that you used in the main function as shown (this is also going to turn off warning and error messages, more info here):
//(From here)
Logger logger = Logger.getLogger(GlobalScreen.class.getPackage().getName());
logger.setLevel(Level.OFF);
logger.setUseParentHandlers(false);
//(To there-^)
try {
GlobalScreen.registerNativeHook();
}
catch (NativeHookException ex) {
System.err.println("There was a problem registering the native hook.");
System.err.println(ex.getMessage());
System.exit(1);
}
Disclaimer: I know this question was solved years ago, I just hope someone finds this a little easier to find/use.
Have your program open a second window which displays underneath your main window but is maximised, then your errant mouse clicks will all be received by the maximised window, and it can receive your keyboard input.
Here's a pure Java way to do it to solve the problem you've described (not the KeyListener problem... the quit test early when using robot problem):
Throughout your test, compare the mouse position with one that your test has recently set it to. If it doesn't match, quit the test. Note: the important part of this code is the testPosition method. Here's code that I used recently:
public void testSomething() throws Exception {
try {
// snip
// you can even extract this into a method "clickAndTest" or something
robot.mouseMove(x2, y2);
click();
testPosition(x2, y2);
// snip
} catch (ExitEarlyException e) {
// handle early exit
}
}
private static void click() throws InterruptedException {
r.mousePress(InputEvent.BUTTON1_DOWN_MASK);
Thread.sleep(30 + rand.nextInt(50));
r.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
Thread.sleep(30 + rand.nextInt(50));
}
private static void testPosition(int x2, int y2) throws ExitEarlyException {
Point p = MouseInfo.getPointerInfo().getLocation();
if(p.x != x2 || p.y != y2) throw new ExitEarlyException();
}
I am trying to find out an elegant way to make JTable stop cell editing (cancel it actually) when user closes the main application window. I know something like this can be done using the WindowAdapter but for this to work properly I need a reference to the window. Problem is I sometimes do not have it.
An abrupt exit may need to be handled at several levels.
Assuming that the user must navigate out of the table to click on an Exit control, you should get the desired result by setting the table's "terminateEditOnFocusLost" property.
table.putClientProperty("terminateEditOnFocusLost", true);
If you have to resort to a WindowListener or similar, you can stop editing explicitly as shown here.
CellEditor cellEditor = table.getCellEditor();
if (cellEditor != null) {
if (cellEditor.getCellEditorValue() != null) {
cellEditor.stopCellEditing();
} else {
cellEditor.cancelCellEditing();
}
}
Because the host owns the frame decorations, some platforms require special handling to intercept the close control, as discussed here.
Preferences makes a best effort to persist updated values.
So far, this is the solution I am most satisfied with:
All my JTable objects install themselves as HierarchyListener(s) with the following method to handle the HierarchyEvent:
public void hierarchyChanged(HierarchyEvent e) {
Window win = SwingUtilities.getWindowAncestor(this);
if ((win != null) && (!win.equals(topLevelWindow))) {
topLevelWindow = win;
topLevelWindow.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
handleWindowClosing();
}
});
}
}
private void handleWindowClosing() {
if (table.isEditing()) {
table.getCellEditor().cancelCellEditing();
}
}
In the case of the project I work on, cancelling the editing when the application window closes is crucial because it sends the notification that the record is no longer in the editing state...
Using hierarchy listener is also crucial because my JTable objects move from dockable to dockable (we use the excellent Docking Frames here) and can be in different windows at different times.
I need to create a JDialog, or a JFrame class (it doesn't particularly matter so long as it's a graphical interface) - that can be used in a static command that is called from the within the actionPerformed(ActionEvent) handler and will stall that process until the user provides their selections from the user interface.
Obviously it is possible to do, because this is exactly what the dialogs JOptionPane creates does. But when I try to stall the thread until the user has completed the selection process, the interface doesn't display properly.
Here is the part of my code that I'm having trouble with:
public static Results queryForResults(String message, int propertyOne,
int propertyTwo){
MyCustomDialog mcd = new MyCustomDialog(message, propertyOne,propertyTwo);
mcd.show();
// complete() is a boolean property of my MyCustomDialog class that turns
// true when the interface has been completed.
while(!mcd.complete()){
synchronized(mcd){
try{
mcd.wait(10000L);
} catch(InterruptedException e) {
e.printStackTrace();
return null;
}
}
}
mcd.dispose();
// My MyCustomDialog class has a getResults() property which returns
// a Results object (i.e. another custom class I've made to contain
// the selections made by the user.)
return mcd.getResults();
}
The dialog outer frame appears, but it's insides don't. They just seem to be a screen capture of whatever was on the screen beneath the dialog when it appeared. I get the impression that this isn't working because I'm not supposed to be stalling the Swing Event thread with the wait command. So how do you do it?
There is no need for a while loop. A modal JDialog will cause execution to stop in your ActionLIstener until the dialog is closed.
Also, don't use the show() method to display a dialog. You should be using the setVisible(true) method.
How do you close a java application from the code?
You call System.exit:
System.exit(0);
I believe that by most standards, System.exit() is a not very OOP way of closing applications, I've always been told that the proper way is to return from main. This is somewhat a bit of a pain and requires a good design but I do believe its the "proper" way to exit
If you're terminating a Swing app, I would do an EXIT_ON_CLOSE
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
before System.exit(0). This is better since you can write a Window Listener to make some cleaning operations before actually leaving the app.
That window listener allows you to exit the app from the code:
public void windowClosing(WindowEvent e) {
displayMessage("WindowListener method called: windowClosing.");
//A pause so user can see the message before
//the window actually closes.
ActionListener task = new ActionListener() {
boolean alreadyDisposed = false;
public void actionPerformed(ActionEvent e) {
if (frame.isDisplayable()) {
alreadyDisposed = true;
frame.dispose();
}
}
};
Timer timer = new Timer(500, task); //fire every half second
timer.setInitialDelay(2000); //first delay 2 seconds
timer.setRepeats(false);
timer.start();
}
public void windowClosed(WindowEvent e) {
//This will only be seen on standard output.
displayMessage("WindowListener method called: windowClosed.");
}
If you're running an application, System.exit will work.
System.exit(int);
In an applet, however, you'll have to do something along the lines of applet.getAppletContext().showDocument("landingpage.html"); because of browser permissions. It won't just let you close the browser window.
You use System.exit(int), where a value of 0 means the application closed successfully and any other value typically means something was wrong. Usually you just see a return value of 1 along with a message printed to sysout or syserr if the application did not close successfully.
Everything is fine, application shut down correctly:
System.exit(0)
Something went wrong, application did not shut down correctly:
System.err.println("some meaningful message"); System.exit(1)
I want to get my program to unhide main window when user presses some shortcut. Is there a way to get the global key events, not only the ones which happened when focus was inside application frame?
This might do what you want. Note that this code is checking for a Ctr-F keystroke. I use this code to open up a find dialog from anything in the application. I'm pretty sure that the app has to have focus though. Something to try at least...
AWTEventListener listener = new AWTEventListener() {
#Override
public void eventDispatched(AWTEvent event) {
try {
KeyEvent evt = (KeyEvent)event;
if(evt.getID() == KeyEvent.KEY_PRESSED && evt.getModifiers() == KeyEvent.CTRL_MASK && evt.getKeyCode() == KeyEvent.VK_F) {
}
}
catch(Exception e) {
e.printStackTrace();
}
}
};
Toolkit.getDefaultToolkit().addAWTEventListener(listener, AWTEvent.KEY_EVENT_MASK);
EDIT: I think I understand what you want. Basically when the app does NOT have focus. If so then you'll probably have to hook into the OS events with a native API (JNI) but that forces you to a specific OS...
This might be useful. I'm not sure if there is one library that will work for Windows/Linux/Mac. For Windows you will need some external library that uses native code to create a keyboard hook. I have no idea how to do it on the other OSes.
A solution to do this by using a JFrame is to set his opacity to 0.0 and to add the Keylistener to it. But the user will see an icon in his shortcut bar...