Skip to main content

How Do I Generate a KeyEvent Programmatically? (Code with StackTrace)

1 reply [Last post]
mike__rainville
Offline
Joined: 2004-06-14
Points: 0

In a Swing database application, on Windows XP SP 3, using EclipseLink 2.0.3 JPA 2, and SwingX 1.6.2, and BeansBinding 1.2.1, I display a few rows from many tables, 5 to 16 at a time, using a data viewer component (JXTaskPane, JScrollPane, JTable, TypedQuery and ObservableList, JTables are bound to Lists from queries), one object per table, in JXTaskPaneContainers, some inside JSplitPanes. When fetching the data via JPA TypedQuery, a variable and unpredictable number of data viewer objects are populated (usually the same for a particular search key). These JXTaskPanes are set to expand if there are any rows, and collapse and be set invisible, if there are no rows.

Selecting a name from a JTable causes all the queries to be run using the associated ID key. The workaround below effectively rereads all the queries and reloads all the data viewers.

When I press Control + SPACE twice, to deselect and then select the person's name again (JTable ListSelectionModel), it causes all the queries to be repeated, after which everything is displayed, as expected. I am trying to make the workaround below create the double Control + SPACE programmatically, to reproduce the effect without user intervention. A trace follows the code...

Do you have any suggestions, as to what's wrong with the key generation code below, or what might be happening with JPA or SwingX?

<code>
final boolean WORKAROUND_IN_EFFECT = true; /* When false the compiler will remove all of this code */
if (WORKAROUND_IN_EFFECT) {
// Problem: When a name is selected, automatically or by the user, the DataViewers do not all open to display their data, cause unknown.
// However, when you type (press and release) "Control+ Space" TWICE, all DataViewers open as expected. The workaround below does this under program control.
new Thread( new Runnable() {
public void run() {

final Toolkit toolkit = Toolkit.getDefaultToolkit();
final EventQueue eventQueue = toolkit.getSystemEventQueue();
//final char controlSpaceCharacter = ( KeyStroke.getKeyStroke( KeyEvent.VK_SPACE, InputEvent.CTRL_DOWN_MASK ) ).getKeyChar();

try {

Thread.sleep(1500);

} catch(InterruptedException ie) { }
catch( Exception e1 ) {
System.out.println( "Unexpected exception in Control Spacebar(name entry,deselect)workaround: "+ e1.getMessage() );
e1.printStackTrace();
} finally {

// JTable "toggleAndAnchor" Action is mapped to [Control]+[Space bar] first DESELECT
eventQueue.postEvent( new KeyEvent( namesViewer
, KeyEvent.KEY_TYPED
, System.currentTimeMillis()
, InputEvent.CTRL_MASK
, KeyEvent.VK_SPACE
// , KeyStroke.getKeyStroke( "control SPACE")
) // end KeyEvent( Component,KeyEvent, time, InputEvent mask, KeyEvent key, char keyValue )
); // end invoke postEvent( KeyEvent )
} // end try (first, DESELECT)
try {

Thread.sleep(1500);

} catch(InterruptedException ie) {

} catch( Exception e2 ) {
System.out.println( "Unexpected exception in Control Spacebar(name entry,reselect) workaround: "+ e2.getMessage() );
e2.printStackTrace();
} finally {
// JTable "toggleAndAnchor" Action is mapped to [Control]+[Space bar] second RESELECT
eventQueue.postEvent( new KeyEvent( namesViewer
, KeyEvent.KEY_TYPED
, System.currentTimeMillis()
, InputEvent.CTRL_MASK
, KeyEvent.VK_SPACE
// , ((KeyStroke.getKeyStroke( KeyEvent.VK_SPACE, InputEvent.CTRL_MASK ) ).getKeyChar())
) // end KeyEvent( Component,KeyEvent, time, InputEvent mask, KeyEvent key, char keyValue )
); // end invoke postEvent( KeyEvent )
} // end try (second, RESELECT)
} // end method run()
} // end class Runnable()

).start();
} // end if WORKAROUND
<code>

SEVERE: Uncaught exception invalid keyCode, in thread Thread[Thread-6,6,main]; see stack trace below.
[java] Jun 10, 2011 3:43:58 PM ca.university.dept.application.Application$6 uncaughtException
[java] SEVERE: java.lang.IllegalArgumentException: invalid keyCode
[java] at java.awt.event.KeyEvent.<init>(KeyEvent.java:949)
[java] at java.awt.event.KeyEvent.<init>(KeyEvent.java:1002)
[java] at java.awt.event.KeyEvent.<init>(KeyEvent.java:1012)
[java] at ca.university.dept.application.Application$10.run(Unknown Source)
[java] at java.lang.Thread.run(Thread.java:662)

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
mike__rainville
Offline
Joined: 2004-06-14
Points: 0

I solved the exception problem ...
from Java 6 API documentation here download.oracle.com/javase/6/docs/api/java/awt/event/KeyEvent.html
I noticed "For key pressed and key released events, the getKeyCode method returns the event's keyCode. For key typed events, the getKeyCode method always returns VK_UNDEFINED."
So I changed both occurrences of KeyEvent.KEY_TYPED to KeyEvent.KEY_RELEASED in the code, which solved the exception.
I also changed the target from the "namesViewer" to the application, which definitely works, but not exactly the same as when I enter the double Control+SPACE by hand.
Here's the revised code
[code]
if (WORKAROUND_IN_EFFECT) {
// Problem: When a name is selected, automatically or by the user, the DataViewers do not all open to display their data, cause unknown.
// However, when you type (press and release) "Control+ Space" TWICE, all DataViewers open as expected. The workaround below does this under program control.
new Thread( new Runnable() {
@SuppressWarnings("deprecation")
public void run() {

final Toolkit toolkit = Toolkit.getDefaultToolkit();
final EventQueue eventQueue = toolkit.getSystemEventQueue();
//final char controlSpaceCharacter = ( KeyStroke.getKeyStroke( KeyEvent.VK_SPACE, InputEvent.CTRL_DOWN_MASK ) ).getKeyChar();
final Component targetComponent = app; // was OK with namesViewer.getParent().getParent().getParent()
try {

Thread.sleep(1500);

} catch(InterruptedException ie) { }
catch( Exception e1 ) {
System.out.println( "Unexpected exception in Control Spacebar(name entry,deselect)workaround: "+ e1.getMessage() );
e1.printStackTrace();

} finally {
// change target from namesViewer to its parent
// JTable "toggleAndAnchor" Action is mapped to [Control]+[Space bar] first DESELECT
eventQueue.postEvent( new KeyEvent( targetComponent
, KeyEvent.KEY_RELEASED
, System.currentTimeMillis()
, InputEvent.CTRL_DOWN_MASK
, KeyEvent.VK_SPACE
// , KeyStroke.getKeyStroke( "control SPACE")
) // end KeyEvent( Component,KeyEvent, time, InputEvent mask, KeyEvent key, char keyValue )
); // end invoke postEvent( KeyEvent )
} // end try (first, DESELECT)
try {

Thread.sleep(1500);

} catch(InterruptedException ie) {

} catch( Exception e2 ) {
System.out.println( "Unexpected exception in Control Spacebar(name entry,reselect) workaround: "+ e2.getMessage() );
e2.printStackTrace();
} finally {
// JTable "toggleAndAnchor" Action is mapped to [Control]+[Space bar] second RESELECT
eventQueue.postEvent( new KeyEvent( targetComponent
, KeyEvent.KEY_RELEASED
, System.currentTimeMillis()
, InputEvent.CTRL_DOWN_MASK
, KeyEvent.VK_SPACE
// , ((KeyStroke.getKeyStroke( KeyEvent.VK_SPACE, InputEvent.CTRL_MASK ) ).getKeyChar())
) // end KeyEvent( Component,KeyEvent, time, InputEvent mask, KeyEvent key, char keyValue )
); // end invoke postEvent( KeyEvent )
} // end try (second, RESELECT)
} // end method run()
} // end class Runnable()

).start();
} // end if WORKAROUND
[code]
======================== ANOTHER APPROACH ================================
FYI,

The JTables are made using BeansBinding 1.2.1 to connect a JTable with the List from JAP TypedQuery

e.g. See here
Beans Binding Via The Road Less Travelled By (Part 1)
By Geertjan on Feb 11, 2008

http://blogs.oracle.com/geertjan/entry/beans_binding_via_the_road by

except that BindingGroup is not used (provided byJTableBinding) and my JTables are reused (by unbind()), change the List, then bind()

This is another approach to the same problem (Enter Control+SPACE) as a workaround, to force a requery of all entities for a specific ID No. Control+SPACE corresponds to toggleAndAnchor in JTable, and "fires" the listSelectionEvent, (de-select and re-select) which executes populateAll() to load many JTables from JPA Typed Queries.

The Java Robot class addresses keystrokes to the application, as though they came from Windows XP.

Problems with this approach :

(1)the Robot class seems to linger after the application is closed, causing some very wild misbehaviour in Windows applications, like not being able to scroll backward through the names list

(2)the performance is really unacceptable; it really interferes with normal Swing operation (e.g. I can navigate forward with the workaround active, but not backward (in a single column JTable - a lists of names)

here is the whole thing as a method. (I am posting this to help clarify what I am trying to accomplish, as well as hoping that someone has had similar problems with large nos of tables in JPA 2 might know a JPA alternative )

<code>

public static synchronized void controlSpaceTwice(){
boolean WORKAROUND = true;

if (WORKAROUND) {

Executor singleThreadExecutor = Executors.newFixedThreadPool(1);

Runnable controlSpaceTwice = new Runnable() {
public void run() {
Robot robot;
final Component componentOriginallyInFocus = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();

try {
robot = new Robot();

robot.setAutoDelay( 200 ); // (0.2 seconds) milliseconds to sleep after generating an event.
robot.setAutoWaitForIdle(true); // automatically invoke "waitForIdle" after generating an event
robot.delay(500); //(.5 seconds) make sure the "populateAll" is really finished

Application.namesViewer.requestFocus();

for (int repeat= 1; repeat < 3; repeat++) {

/* Hold down the Control Key */
robot.keyPress( KeyEvent.VK_CONTROL );

/* Type a Space */
robot.keyPress( KeyEvent.VK_SPACE );
robot.keyRelease( KeyEvent.VK_SPACE );

/* Release the Control Key */
robot.keyRelease( KeyEvent.VK_CONTROL );

} // end loop (repeat twice)

} catch (AWTException e) {
System.out.println("WARNING: Workaround internal keystroke entry (Control+SPACE) failed;"
+"\n Java reason was" + e.getMessage() );
e.printStackTrace();
} finally {
componentOriginallyInFocus.requestFocus();
} // end try
} // end method run()
}; // end Runnable()
singleThreadExecutor.execute( controlSpaceTwice );

} // end if WORKAROUND
} // end controlSpaceTwice()

<code>