2 * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
28 import java.util.LinkedList;
29 import sun.awt.AppContext;
30 import sun.awt.SunToolkit;
33 * A mechanism for ensuring that a series of AWTEvents are executed in a
34 * precise order, even across multiple AppContexts. The nested events will be
35 * dispatched in the order in which their wrapping SequencedEvents were
36 * constructed. The only exception to this rule is if the peer of the target of
37 * the nested event was destroyed (with a call to Component.removeNotify)
38 * before the wrapping SequencedEvent was able to be dispatched. In this case,
39 * the nested event is never dispatched.
41 * @author David Mendenhall
43 class SequencedEvent extends AWTEvent implements ActiveEvent {
47 private static final long serialVersionUID = 547742659238625067L;
49 private static final int ID =
50 java.awt.event.FocusEvent.FOCUS_LAST + 1;
51 private static final LinkedList list = new LinkedList();
53 private final AWTEvent nested;
54 private AppContext appContext;
55 private boolean disposed;
58 * Constructs a new SequencedEvent which will dispatch the specified
61 * @param nested the AWTEvent which this SequencedEvent's dispatch()
62 * method will dispatch
64 public SequencedEvent(AWTEvent nested) {
65 super(nested.getSource(), ID);
67 // All AWTEvents that are wrapped in SequencedEvents are (at
68 // least currently) implicitly generated by the system
69 SunToolkit.setSystemGenerated(nested);
70 synchronized (SequencedEvent.class) {
76 * Dispatches the nested event after all previous nested events have been
77 * dispatched or disposed. If this method is invoked before all previous nested events
78 * have been dispatched, then this method blocks until such a point is
80 * While waiting disposes nested events to disposed AppContext
82 * NOTE: Locking protocol. Since dispose() can get EventQueue lock,
83 * dispatch() shall never call dispose() while holding the lock on the list,
84 * as EventQueue lock is held during dispatching. The locks should be acquired
87 public final void dispatch() {
89 appContext = AppContext.getAppContext();
91 if (getFirst() != this) {
92 if (EventQueue.isDispatchThread()) {
93 EventDispatchThread edt = EventDispatchThread.findCurrent();
94 edt.pumpEvents(SentEvent.ID, new Conditional() {
95 public boolean evaluate() {
96 return !SequencedEvent.this.isFirstOrDisposed();
100 while(!isFirstOrDisposed()) {
101 synchronized (SequencedEvent.class) {
103 SequencedEvent.class.wait(1000);
104 } catch (InterruptedException e) {
113 KeyboardFocusManager.getCurrentKeyboardFocusManager().
114 setCurrentSequencedEvent(this);
115 Toolkit.getEventQueue().dispatchEvent(nested);
123 * true only if event exists and nested source appContext is disposed.
125 private final static boolean isOwnerAppContextDisposed(SequencedEvent se) {
127 Object target = se.nested.getSource();
128 if (target instanceof Component) {
129 return ((Component)target).appContext.isDisposed();
136 * Sequenced events are dispatched in order, so we cannot dispatch
137 * until we are the first sequenced event in the queue (i.e. it's our
138 * turn). But while we wait for our turn to dispatch, the event
139 * could have been disposed for a number of reasons.
141 public final boolean isFirstOrDisposed() {
145 // getFirstWithContext can dispose this
146 return this == getFirstWithContext() || disposed;
149 private final synchronized static SequencedEvent getFirst() {
150 return (SequencedEvent)list.getFirst();
153 /* Disposes all events from disposed AppContext
154 * return first valid event
156 private final static SequencedEvent getFirstWithContext() {
157 SequencedEvent first = getFirst();
158 while(isOwnerAppContextDisposed(first)) {
166 * Disposes of this instance. This method is invoked once the nested event
167 * has been dispatched and handled, or when the peer of the target of the
168 * nested event has been disposed with a call to Component.removeNotify.
170 * NOTE: Locking protocol. Since SunToolkit.postEvent can get EventQueue lock,
171 * it shall never be called while holding the lock on the list,
172 * as EventQueue lock is held during dispatching and dispatch() will get
173 * lock on the list. The locks should be acquired in the same order.
175 final void dispose() {
176 synchronized (SequencedEvent.class) {
180 if (KeyboardFocusManager.getCurrentKeyboardFocusManager().
181 getCurrentSequencedEvent() == this) {
182 KeyboardFocusManager.getCurrentKeyboardFocusManager().
183 setCurrentSequencedEvent(null);
188 if (appContext != null) {
189 SunToolkit.postEvent(appContext, new SentEvent());
192 SequencedEvent next = null;
194 synchronized (SequencedEvent.class) {
195 SequencedEvent.class.notifyAll();
197 if (list.getFirst() == this) {
200 if (!list.isEmpty()) {
201 next = (SequencedEvent)list.getFirst();
207 // Wake up waiting threads
208 if (next != null && next.appContext != null) {
209 SunToolkit.postEvent(next.appContext, new SentEvent());