JDK-4434193 : ActionEvents (and other events) need timestamps
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 1.3.0,1.4.0
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic,windows_nt
  • CPU: generic,x86
  • Submitted: 2001-04-05
  • Updated: 2014-07-01
  • Resolved: 2001-07-31
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
1.4.0 beta2Fixed
Related Reports
Duplicate :  
Relates :  
Relates :  
Description
if in EventDispatchThread we are processing a ActionEvent
the EventQueue.getDispatchingEventTime returns current system time.
it's possible that this time value is greater than the timestamp of
KeyEvents posted later than the ActionEvent being dispatched.
so if we use this time value to call 
DefaultKeyboardFocusManager.enqueueKeyEvents
we might not get the desired behavior(hold any keyEvent which is
posted after the current event)

tao.ma@Eng 2001-04-04

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: merlin-beta2 FIXED IN: merlin-beta2 INTEGRATED IN: merlin-beta2
14-06-2004

EVALUATION Needed to correctly fix regression from kestrel. david.mendenhall@east 2001-04-05 Release: merlin-beta-refresh Problem: (BugIDs 4434193 and 4026963) ActionEvents (and other events) need timestamps getModifiers() in class ActionEvent always returns 0 (JDC Top 25 Bug) The new focus architecture includes a type-ahead mechanism that ensures that subsequent KeyEvents that follow a KeyEvent that initiates a focus transfer are not delivered until the transfer is completed. The design for this feature is based on the UTC timestamps of the various events. Events with timestamps later than that of the initiating event are enqueued pending resolution of the transfer; events with earlier timestamps are not. To implement this feature, the focus code keeps track of the timestamp of the event currently being handled. If a focus change is initiated during this handling, the timestamp is available for use. However, if the current event does not have a timestamp, then the current system time is used. This time is usually too far ahead of the time that the event actually occurred to be of any real use. As a result, the type-ahead mechanism fails, and KeyEvents are delivered before the focus transfer is completed. The most common case where we encounter this problem is with ActionEvents. ActionEvents are high-level, semantic events generated in response to underlying InputEvents. While the InputEvents have timestamps associated with them, the ActionEvents do not. The ActionEvent API must be expanded to accommodate a timestamp, and the implementation must be updated so that an ActionEvent's timestamp is equal to that of its underlying InputEvent. Another common case that exhibits the same problem is with InvocationEvents synthesized from calls to EventQueue.invokeLater(Runnable), EventQueue.invokeAndWait(Runnable), SwingUtilities.invokeLater(Runnable), and SwingUtilities.invokeAndWait(Runnable). Therefore, the InvocationEvent API must also be expanded to accommodate a timestamp. A final important case is with InputMethodEvents. These are roughly equivalent to InputEvents, as they are generated in direct response to a user's actions. However, unlike InputEvents, InputMethodEvents currently do not have timestamps. The InputMethodEvent API must be expanded to accommodate a timestamp. For InputMethodEvents that are generated by a native, host input method, the timestamp must be equal to the timestamp of the underlying native event. For InputMethodEvents that are generated by a Java input method, the timestamp must be equal to the underlying InputEvent. In addition, we must address the issue that Swing often synthesizes ActionEvents from methods that do not have access to the underlying InputEvent. While we could add new versions of these methods that take an InputEvent as an argument, this would require developers to update existing applications to invoke the new versions of these methods. Without such an update, Swing would be forced to synthesize ActionEvents with nonsensical timestamps. An alternate approach is for the AWT to store the timestamp of the last InputEvent, ActionEvent, InvocationEvent, or InputMethodEvent that was dispatched. Because Swing is an event-driven architecture, ActionEvents are only synthesized as part of the processing of another event. Typically, the event currently being processed at such times has a timestamp, so the stored value is exactly correct. In some edge cases, the event currently being processed is of some other type; however, because all events are fundamentally generated as a result of an event that has a timestamp, the timestamp of the last such event is appropriate. This value is also appropriate as the timestamp for all InvocationEvents, and for ActionEvents and InputMethodEvents that are instantiated without an explicit timestamp (using the existing constructors, rather than the new ones specified in this document). If this same approach is applied to the current event in general, we can also resolve a long-standing JDC Top 25 bug. BugID 4026963 documents a bug in the AWT and Swing wherein almost all ActionEvents have a 'modifiers' field of zero, even if the underlying event had a non-zero 'modifiers' field. While many of the problem cases can be corrected without an API change, several of the Swing cases cannot be fixed because, as with the timestamps, the code synthesizing the ActionEvent does not have access to the underlying event. Requesters: AWT Core (David Mendenhall) I18N (Naoto Sato) CTE (Tao Ma) Proposed API change: Add the following methods to java.awt.event.ActionEvent: /** * Constructs an <code>ActionEvent</code> object with the specified * modifier keys and timestamp. * <p> * Note that passing in an invalid <code>id</code> results in unspecified * behavior. * * @param source the object that originated the event * @param id an integer that identifies the event * @param command a string that may specify a command (possibly one * of several) associated with the event * @param when the time the event occurred * @param modifiers the modifier keys held down during this action * * @since 1.4 */ public ActionEvent(Object source, int id, String command, long when, int modifiers); /** * Returns the timestamp of when this event occurred. Because an * ActionEvent is a high-level, semantic event, the timestamp is typically * the same as an underlying InputEvent. * * @return this event's timestamp * @since 1.4 */ public long getWhen(); /** * Initializes the <code>when</code> field if it is not present in the * object input stream. In that case, the field will be initialized by * invoking {@link java.awt.EventQueue#getMostRecentEventTime()}. */ private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException; Modify the following existing methods of java.awt.event.ActionEvent as follows: /** ! * Constructs an <code>ActionEvent</code> object. The timestamp for this ! * event is initialized by invoking ! * {@link java.awt.EventQueue#getMostRecentEventTime()}. * <p> * Note that passing in an invalid <code>id</code> results in unspecified * behavior. * * @param source the object that originated the event * @param id an integer that identifies the event * @param command a string that may specify a command (possibly one * of several) associated with the event */ public ActionEvent(Object source, int id, String command); /** ! * Constructs an <code>ActionEvent</code> object with modifier keys. The ! * timestamp for this event is initialized by invoking ! * {@link java.awt.EventQueue#getMostRecentEventTime()}. * <p> * Note that passing in an invalid <code>id</code> results in unspecified * behavior. * * @param source the object that originated the event * @param id an integer that identifies the event * @param command a string that may specify a command (possibly one * of several) associated with the event * @param modifiers the modifier keys held down during this action */ public ActionEvent(Object source, int id, String command, int modifiers); Add the following method to java.awt.event.InvocationEvent: /** * Returns the timestamp of when this event occurred. * * @return this event's timestamp * @since 1.4 */ public long getWhen(); /** * Initializes the <code>when</code> field if it is not present in the * object input stream. In that case, the field will be initialized by * invoking {@link java.awt.EventQueue#getMostRecentEventTime()}. */ private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException; Modify the following existing methods of java.awt.event.InvocationEvent as follows: /** * Constructs an <code>InvocationEvent</code> with the specified * source which will execute the runnable's <code>run</code> ! * method when dispatched. The timestamp for this event is initialized by ! * invoking {@link java.awt.EventQueue#getMostRecentEventTime()}. * * @param source the <code>Object</code> that originated the event * @param runnable the <code>Runnable</code> whose <code>run</code> * method will be executed */ public InvocationEvent(Object source, Runnable runnable); /** * Constructs an <code>InvocationEvent</code> with the specified source * which will execute the runnable's <code>run</code> method when * dispatched. If notifier is non-<code>null</code>, * <code>notifyAll()</code> will be called on it immediately after ! * <code>run</code> returns. The timestamp for this event is initialized by ! * invoking {@link java.awt.EventQueue#getMostRecentEventTime()}. * * @param source the <code>Object</code> that originated * the event * @param runnable the <code>Runnable</code> whose * <code>run</code> method will be * executed * @param notifier the Object whose <code>notifyAll</code> * method will be called after * <code>Runnable.run</code> has returned * @param catchExceptions specifies whether <code>dispatch</code> * should catch Exception when executing * the <code>Runnable</code>'s <code>run</code> * method, or should instead propagate those * Exceptions to the EventDispatchThread's * dispatch loop */ public InvocationEvent(Object source, Runnable runnable, Object notifier, boolean catchExceptions); Add the following methods to java.awt.event.InputMethodEvent: /** * Constructs an <code>InputMethodEvent</code> with the specified source * component, type, time, text, caret, and visiblePosition. * <p> * The offsets of caret and visiblePosition are relative to the current * composed text; that is, the composed text within <code>text</code> if * this is an <code>INPUT_METHOD_TEXT_CHANGED</code> event, the composed * text within the <code>text</code> of the preceding * <code>INPUT_METHOD_TEXT_CHANGED</code> event otherwise. * * @param source the object where the event originated * @param id the event type * @param when a long integer that specifies the time the event occurred * @param text the combined committed and composed text, * committed text first; must be <code>null</code> * when the event type is <code>CARET_POSITION_CHANGED</code>; * may be <code>null</code> for * <code>INPUT_METHOD_TEXT_CHANGED</code> if there's no * committed or composed text * @param committedCharacterCount the number of committed * characters in the text * @param caret the caret (a.k.a. insertion point); * <code>null</code> if there's no caret within current * composed text * @param visiblePosition the position that's most important * to be visible; <code>null</code> if there's no * recommendation for a visible position within current * composed text * @exception IllegalArgumentException if <code>id</code> is not * in the range * <code>INPUT_METHOD_FIRST</code>..<code>INPUT_METHOD_LAST</code>; * or if id is <code>CARET_POSITION_CHANGED</code> and * <code>text</code> is not <code>null</code>; * or if <code>committedCharacterCount</code> is not in the range * <code>0</code>..<code>(text.getEndIndex() - text.getBeginIndex())</code> * * @since 1.4 */ public InputMethodEvent(Component source, int id, long when, AttributedCharacterIterator text, int committedCharacterCount, TextHitInfo caret, TextHitInfo visiblePosition); /** * Returns the timestamp of when this event occurred. * * @return this event's timestamp * @since 1.4 */ public long getWhen(); /** * Initializes the <code>when</code> field if it is not present in the * object input stream. In that case, the field will be initialized by * invoking {@link java.awt.EventQueue#getMostRecentEventTime()}. */ private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException; Modify the specification of the following existing methods on java.awt.event.InputMethodEvent as follows: /** * Constructs an <code>InputMethodEvent</code> with the specified source * component, type, text, caret, and visiblePosition. * <p> * The offsets of caret and visiblePosition are relative to the current * composed text; that is, the composed text within <code>text</code> if * this is an <code>INPUT_METHOD_TEXT_CHANGED</code> event, the composed * text within the <code>text</code> of the preceding ! * <code>INPUT_METHOD_TEXT_CHANGED</code> event otherwise. The time stamp ! * for this event is initialized by invoking ! * {@link java.awt.EventQueue#getMostRecentEventTime()}. * * @param source the object where the event originated * @param id the event type * @param text the combined committed and composed text, * committed text first; must be <code>null</code> * when the event type is <code>CARET_POSITION_CHANGED</code>; * may be <code>null</code> for * <code>INPUT_METHOD_TEXT_CHANGED</code> if there's no * committed or composed text * @param committedCharacterCount the number of committed * characters in the text * @param caret the caret (a.k.a. insertion point); * <code>null</code> if there's no caret within current * composed text * @param visiblePosition the position that's most important * to be visible; <code>null</code> if there's no * recommendation for a visible position within current * composed text * @exception IllegalArgumentException if <code>id</code> is not * in the range * <code>INPUT_METHOD_FIRST</code>..<code>INPUT_METHOD_LAST</code>; * or if id is <code>CARET_POSITION_CHANGED</code> and * <code>text</code> is not <code>null</code>; * or if <code>committedCharacterCount</code> is not in the range * <code>0</code>..<code>(text.getEndIndex() - text.getBeginIndex())</code> */
11-06-2004

EVALUATION public InputMethodEvent(Component source, int id, AttributedCharacterIterator text, int committedCharacterCount, TextHitInfo caret, TextHitInfo visiblePosition); /** * Constructs an <code>InputMethodEvent</code> with the specified source * component, type, caret, and visiblePosition. The text is set to * <code>null</code>, <code>committedCharacterCount</code> to 0. * <p> * The offsets of <code>caret</code> and <code>visiblePosition</code> are * relative to the current composed text; that is, the composed text within * the <code>text</code> of the preceding * <code>INPUT_METHOD_TEXT_CHANGED</code> event if the event being * constructed as a <code>CARET_POSITION_CHANGED</code> event. For an * <code>INPUT_METHOD_TEXT_CHANGED</code> event without text, * <code>caret</code> and <code>visiblePosition</code> must be ! * <code>null</code>. The time stamp for this event is initialized by ! * invoking {@link java.awt.EventQueue#getMostRecentEventTime()}. * * @param source the object where the event originated * @param id the event type * @param caret the caret (a.k.a. insertion point); * <code>null</code> if there's no caret within current * composed text * @param visiblePosition the position that's most important * to be visible; <code>null</code> if there's no * recommendation for a visible position within current * composed text * @exception IllegalArgumentException if <code>id</code> is not * in the range * <code>INPUT_METHOD_FIRST</code>..<code>INPUT_METHOD_LAST</code> */ public InputMethodEvent(Component source, int id, TextHitInfo caret, TextHitInfo visiblePosition); Add the following methods to java.awt.EventQueue: /** * Returns the timestamp of the most recent event that had a timestamp, and * that was dispatched from the <code>EventQueue</code> associated with the * calling thread. If an event with a timestamp is currently being * dispatched, its timestamp will be returned. In the current version of * the Java platform SDK, only <code>InputEvent</code>s, * <code>ActionEvent</code>s, <code>InvocationEvent</code>s, and * <code>InputMethodEvent</code>s have timestamps; however, future versions * of the SDK may add timestamps to additional event types. Note that this * method should only be invoked from an application's event dispatching * thread. If this method is invoked from another thread, the current * system time (as reported by <code>System.currentTimeMillis()</code>) * will be returned instead. * * @return the timestamp of the last <code>InputEvent</code>, * <code>ActionEvent</code>, <code>InvocationEvent</code>, or * <code>InputMethodEvent</code> to be dispatched, or * <code>System.currentTimeMillis()</code> if this method is * invoked on a thread other than an event dispatching thread * @see java.awt.event.InputEvent#getWhen * @see java.awt.event.ActionEvent#getWhen * @see java.awt.event.InvocationEvent#getWhen * @see java.awt.event.InputMethodEvent#getWhen * * @since 1.4 */ public static long getMostRecentEventTime(); /** * Returns the the event currently being dispatched by the * <code>EventQueue</code> associated with the calling thread. This is * useful if a method needs access to the event, but was not designed to * receive a reference to it as an argument. Note that this method should * only be invoked from an application's event dispatching thread. If this * method is invoked from another thread, null will be returned. * * @return the event currently being dispatched, or null if this method is * invoked on a thread other than an event dispatching thread * @since 1.4 */ public static AWTEvent getCurrentEvent(); API reviewed and approved by: awt-core@eng swing-eng@eng java-i18n@eng Implementation: - Engineer who made (or will make) the changes: David Mendenhall, Naoto Sato - Date at which changes will be complete: Immediately - Number of lines of new or modified code: + Java: TBD + Native: TBD - Code reviewed (or will be reviewed) by: TBD Risk assessment: Low. This API change should be purely an addition of code, and should not affect any existing code. Any bugs introduced would almost certainly be confined to the new feature itself. SQE impact: TBD JCK impact: TBD Doc impact: javadoc changes only. Localization impact: None. Internationalization impact: None. Security impact: None. Legal impact: None. For feature changes, Product Marketing approval: N/A david.mendenhall@east 2001-06-20
20-06-2001

SUGGESTED FIX according to David Mendenhall, ActionEvent should have a getWhen method, and DefaultKeyboardFocusManager should be updated appropriatly. tao.ma@Eng 2001-04-04
04-04-2001