This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 194147
Collapse All | Expand All

(-)a/openide.windows/src/org/netbeans/modules/openide/windows/ApplicationActivationManagerImpl.java (+477 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2011 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s): Tim Boudreau
39
 *
40
 * Portions Copyrighted 2011 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.modules.openide.windows;
43
44
import java.awt.AWTEvent;
45
import java.awt.EventQueue;
46
import java.awt.KeyboardFocusManager;
47
import java.awt.Toolkit;
48
import java.awt.Window;
49
import java.awt.event.AWTEventListener;
50
import java.awt.event.MouseEvent;
51
import java.beans.PropertyChangeEvent;
52
import java.beans.PropertyChangeListener;
53
import java.lang.ref.Reference;
54
import java.lang.ref.WeakReference;
55
import java.util.Arrays;
56
import java.util.Collections;
57
import java.util.EnumSet;
58
import java.util.Iterator;
59
import java.util.LinkedHashSet;
60
import java.util.LinkedList;
61
import java.util.List;
62
import java.util.Set;
63
import java.util.concurrent.atomic.AtomicBoolean;
64
import java.util.logging.Level;
65
import java.util.logging.Logger;
66
import org.openide.util.Parameters;
67
import org.openide.util.RequestProcessor;
68
import org.openide.util.RequestProcessor.Task;
69
import org.openide.util.lookup.ServiceProvider;
70
import org.openide.windows.ApplicationActivationManager;
71
72
/**
73
 * Default implementation of ApplicationActivationManager.
74
 *
75
 * @author Tim Boudreau
76
 */
77
@ServiceProvider(service = ApplicationActivationManager.class)
78
public class ApplicationActivationManagerImpl extends ApplicationActivationManager {
79
80
    private static final RequestProcessor rp = new RequestProcessor(ApplicationActivationManagerImpl.class);
81
    private static final int DEFAULT_ACTIVATION_DELAY = 800;
82
    private static final int DEFAULT_IDLE_DELAY = 120000; //two minutes - *really* idle
83
    private final Set<ListenerHolder> listeners = Collections.synchronizedSet(new LinkedHashSet<ListenerHolder>());
84
    private final Object stateLock = new Object();
85
    private final Object eventLock = new Object();
86
    private final Object idleLock = new Object();
87
    private final Set<State> state = EnumSet.noneOf(State.class);
88
    private final List<StateEvent> events = new LinkedList<StateEvent>();
89
    private final AtomicBoolean inNotificationLoop = new AtomicBoolean();
90
    //avoid rapid transiently switching to the application, for example
91
    //moving between virtual desktops, to trigger work
92
    private final IdleListener idleListener = new IdleListener();
93
    private final Task addIdleStateTask = rp.create(idleListener);
94
    private final KeyboardFocusListener keyboardFocusManagerListener = new KeyboardFocusListener();
95
    private final Task updateActiveStateTask = rp.create(keyboardFocusManagerListener);
96
    private volatile boolean stateIsIdle;
97
    private volatile boolean listeningForIdle;
98
    private int idleDelay;
99
    private int activationDelay;
100
    private static final String IDLE_SYSTEM_PROPERTY = "application.idle.delay";
101
    private static final String ACTIVATION_SYSTEM_PROPERTY = "application.idle.delay";
102
103
    public ApplicationActivationManagerImpl() {
104
        //check system properties
105
        try {
106
            String prop = System.getProperty(IDLE_SYSTEM_PROPERTY);
107
            if (prop != null) {
108
                idleDelay = Integer.parseInt(prop);
109
                if (idleDelay <= 0) {
110
                    throw new NumberFormatException("Negative " + IDLE_SYSTEM_PROPERTY);
111
                }
112
            } else {
113
                idleDelay = DEFAULT_IDLE_DELAY;
114
            }
115
        } catch (NumberFormatException nfe) {
116
            Logger.getLogger(ApplicationActivationManagerImpl.class.getName()).log(Level.WARNING, IDLE_SYSTEM_PROPERTY, nfe);
117
            idleDelay = DEFAULT_IDLE_DELAY;
118
        }
119
        try {
120
            String prop = System.getProperty(ACTIVATION_SYSTEM_PROPERTY);
121
            if (prop != null) {
122
                activationDelay = Integer.parseInt(prop);
123
            } else {
124
                activationDelay = DEFAULT_ACTIVATION_DELAY;
125
            }
126
            if (activationDelay <= 0) {
127
                throw new NumberFormatException("Negative " + ACTIVATION_SYSTEM_PROPERTY);
128
            }
129
        } catch (NumberFormatException nfe) {
130
            Logger.getLogger(ApplicationActivationManagerImpl.class.getName()).log(Level.WARNING, ACTIVATION_SYSTEM_PROPERTY, nfe);
131
            activationDelay = DEFAULT_ACTIVATION_DELAY;
132
        }
133
        //attach our listener
134
        EventQueue.invokeLater(new Runnable() {
135
136
            @Override
137
            public void run() {
138
                KeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener("focusedWindow", keyboardFocusManagerListener);
139
                boolean active = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow() != null;
140
                if (active) {
141
                    setState(EnumSet.of(State.ACTIVE));
142
                }
143
            }
144
        });
145
    }
146
147
    @Override
148
    public Set<State> getState() {
149
        synchronized (stateLock) {
150
            return EnumSet.copyOf(state);
151
        }
152
    }
153
154
    void setState(Set<State> newState) {
155
        Set<State> oldState;
156
        synchronized (stateLock) {
157
            oldState = getState();
158
            if (newState.equals(oldState)) {
159
                return;
160
            }
161
            this.state.clear();
162
            this.state.addAll(newState);
163
            onChange(this.state);
164
        }
165
        notify(oldState, newState);
166
    }
167
168
    private void notify(Set<State> old, Set<State> nue) {
169
        if (old != null) {
170
            synchronized (eventLock) {
171
                events.add(new StateEvent(old, nue));
172
            }
173
        }
174
        boolean notify = inNotificationLoop.compareAndSet(false, true);
175
        //another caller can already be performing notifications, depending on
176
        //which thread last triggered a state change.  If so, they will be
177
        //delivered
178
        if (notify) {
179
            //list of things to run on whatever thread this is not
180
            final List<Notification> notifyOnOtherThread = new LinkedList<Notification>();
181
            boolean inEQ = EventQueue.isDispatchThread();
182
            try {
183
                final List<StateEvent> notifyEvents = new LinkedList<StateEvent>();
184
                synchronized (eventLock) {
185
                    notifyEvents.addAll(this.events);
186
                    this.events.clear();
187
                }
188
                while (!notifyEvents.isEmpty()) {
189
                    for (StateEvent evt : notifyEvents) {
190
                        for (Iterator<ListenerHolder> it = listeners.iterator(); it.hasNext();) {
191
                            ListenerHolder holder = it.next();
192
                            Listener l = holder.getListener();
193
                            if (l == null) {
194
                                it.remove();
195
                            } else {
196
                                //Filter out states this listener does not care about
197
                                Set<State> oldState = holder.filter(evt.old);
198
                                Set<State> newState = holder.filter(evt.nue);
199
                                //If the result is no change, do not notify
200
                                if (!oldState.equals(newState)) {
201
                                    //Synchronously notify whichever listeners want
202
                                    //to be notified on this thread
203
                                    if (holder.notifyInBackground != inEQ) {
204
                                        try {
205
                                            l.onChange(oldState, newState);
206
                                        } catch (RuntimeException e) {
207
                                            Logger.getLogger(ApplicationActivationManagerImpl.class.getName()).log(Level.SEVERE, null, e);
208
                                        }
209
                                    } else {
210
                                        //add the rest to the queued list, for
211
                                        //notification on a background thread
212
                                        notifyOnOtherThread.add(new Notification(l, new StateEvent(oldState, newState)));
213
                                    }
214
                                }
215
                            }
216
                        }
217
                    }
218
                    //Collect any events that have arrived while
219
                    //we have been looping
220
                    synchronized (eventLock) {
221
                        notifyEvents.clear();
222
                        notifyEvents.addAll(this.events);
223
                        this.events.clear();
224
                    }
225
                }
226
            } finally {
227
                inNotificationLoop.set(false);
228
            }
229
            boolean renotify;
230
            synchronized (eventLock) {
231
                //it is possible for another thread to have entered while the
232
                //loop variable was set to false - so do a quick check to make
233
                //sure we don't have events that should be delivered but won't be
234
                //unless we call ourselves again
235
                renotify = !events.isEmpty();
236
            }
237
            if (!notifyOnOtherThread.isEmpty()) {
238
                //Asynchronously notify any listeners we are on the wrong
239
                //thread for
240
                Runnable r = new Runnable() {
241
242
                    @Override
243
                    public void run() {
244
                        for (Notification l : notifyOnOtherThread) {
245
                            try {
246
                                l.notifyListener();
247
                            } catch (RuntimeException e) {
248
                                Logger.getLogger(ApplicationActivationManagerImpl.class.getName()).log(Level.SEVERE, null, e);
249
                            }
250
                        }
251
                    }
252
                };
253
                if (inEQ) {
254
                    rp.post(r);
255
                } else {
256
                    EventQueue.invokeLater(r);
257
                }
258
            }
259
            if (renotify) {
260
                notify(null, null);
261
            }
262
        }
263
    }
264
265
    private void startListeningForIdleEvents() {
266
        if (!listeningForIdle) {
267
            //ensure we don't have a diff between the value of listeningForIdle
268
            //and whether or not we are actually listening
269
            synchronized (idleLock) {
270
                if (!listeningForIdle) {
271
                    listeningForIdle = true;
272
                    Toolkit.getDefaultToolkit().addAWTEventListener(idleListener, AWTEvent.MOUSE_EVENT_MASK | AWTEvent.KEY_EVENT_MASK);
273
                    addIdleStateTask.schedule(idleDelay);
274
                }
275
            }
276
        }
277
    }
278
279
    private void stopListeningForIdleEvents() {
280
        if (listeningForIdle) {
281
            synchronized (idleLock) {
282
                if (listeningForIdle) {
283
                    addIdleStateTask.cancel();
284
                    Toolkit.getDefaultToolkit().removeAWTEventListener(idleListener);
285
                    listeningForIdle = false;
286
                }
287
            }
288
        }
289
    }
290
291
    private final class IdleListener implements AWTEventListener, Runnable {
292
293
        private volatile long lastEventTime = System.currentTimeMillis();
294
295
        @Override
296
        public void eventDispatched(AWTEvent event) {
297
            int id = event.getID();
298
            //ignore mouse motion
299
            if (id == MouseEvent.MOUSE_MOVED || id == MouseEvent.MOUSE_ENTERED || id == MouseEvent.MOUSE_EXITED || id == MouseEvent.MOUSE_DRAGGED) {
300
                return;
301
            }
302
            lastEventTime = System.currentTimeMillis();
303
            //postpone the idle task
304
            addIdleStateTask.schedule(idleDelay);
305
            if (stateIsIdle) { //avoids the lock in the common case that the state is not idle
306
                synchronized (stateLock) {
307
                    Set<State> states = getState();
308
                    states.remove(State.IDLE);
309
                    //will run notifications under lock
310
                    setState(states);
311
                }
312
            }
313
        }
314
315
        @Override
316
        public void run() {
317
            //run after the idle delay
318
            long timeSinceLastEvent = System.currentTimeMillis() - lastEventTime;
319
            if (timeSinceLastEvent > idleDelay && getState().equals(EnumSet.of(State.ACTIVE))) {
320
                //add the idle state
321
                changeState(State.IDLE);
322
            }
323
        }
324
    }
325
326
    private void onChange(Set<State> states) {
327
        //update the stateIsIdle value so we avoid taking a lock on every
328
        //keystroke / mouse click
329
        stateIsIdle = states.contains(State.IDLE);
330
        if (states.contains(State.ACTIVE)) {
331
            startListeningForIdleEvents();
332
        } else {
333
            stopListeningForIdleEvents();
334
        }
335
    }
336
337
    private static final class StateEvent {
338
339
        private final Set<State> old;
340
        private final Set<State> nue;
341
342
        public StateEvent(Set<State> old, Set<State> nue) {
343
            assert !old.equals(nue);
344
            this.old = old;
345
            this.nue = nue;
346
        }
347
348
        public String toString() {
349
            return old + " to " + nue;
350
        }
351
    }
352
353
    private static final class Notification {
354
355
        private final Listener listener;
356
        private final StateEvent evt;
357
358
        public Notification(Listener listener, StateEvent evt) {
359
            this.listener = listener;
360
            this.evt = evt;
361
        }
362
363
        void notifyListener() {
364
            listener.onChange(evt.old, evt.nue);
365
        }
366
    }
367
368
    @Override
369
    public void addListener(Listener listener, boolean notifyInForeground, State... interestedIn) {
370
        Parameters.notNull("listener", listener);
371
        if (interestedIn.length == 0) {
372
            throw new IllegalArgumentException("No states");
373
        }
374
        listeners.add(new ListenerHolder(listener, notifyInForeground,
375
                EnumSet.copyOf(Arrays.asList(interestedIn))));
376
    }
377
378
    private static final class ListenerHolder {
379
380
        private final Reference<Listener> listenerRef;
381
        private final boolean notifyInBackground;
382
        private final Set<State> interestedIn;
383
384
        public ListenerHolder(Listener listener, boolean notifyInBackground, Set<State> interestedIn) {
385
            this.notifyInBackground = notifyInBackground;
386
            this.interestedIn = interestedIn;
387
            this.listenerRef = new WeakReference<Listener>(listener);
388
        }
389
390
        Listener getListener() {
391
            return listenerRef.get();
392
        }
393
394
        Set<State> filter(Set<State> states) {
395
            Set<State> nue = EnumSet.copyOf(states);
396
            nue.retainAll(interestedIn);
397
            return nue;
398
        }
399
    }
400
401
    private boolean changeState(State toAdd, State... toRemove) {
402
        //add one state and optionally remove others, then update the state
403
        Set<State> oldState;
404
        Set<State> newState;
405
        synchronized (stateLock) {
406
            oldState = getState();
407
            newState = EnumSet.copyOf(oldState);
408
            if (toAdd != null) {
409
                newState.add(toAdd);
410
            }
411
            newState.removeAll(Arrays.asList(toRemove));
412
            if (newState.equals(oldState)) {
413
                return false;
414
            }
415
            this.state.clear();
416
            this.state.addAll(newState);
417
            onChange(this.state);
418
        }
419
        notify(oldState, newState);
420
        return true;
421
    }
422
423
    private final class KeyboardFocusListener implements PropertyChangeListener, Runnable {
424
425
        volatile boolean active;
426
        State destState;
427
428
        @Override
429
        public void propertyChange(PropertyChangeEvent evt) {
430
            //Note one weirdness I've never been able to decipher:  In a regular
431
            //swing application, when you show a dialog, focus goes from the
432
            //parent window to the dialog.  In NetBeans, it goes from the 
433
            //parent window to null to the dialog, and the same when closing.
434
            //This causes a plethora of extra BECOMING_ACTIVE / BECOMING_INACTIVE
435
            //events which are unavoidable
436
            Window old = (Window) evt.getOldValue();
437
            Window nue = (Window) evt.getNewValue();
438
            if ((old == null) != (nue == null)) {
439
                active = nue != null;
440
                if (!active) {
441
                    //add the becoming inactive state, remove becoming active if present
442
                    changeState(State.BECOMING_INACTIVE, State.BECOMING_ACTIVE);
443
                    //set our single destination state
444
                    synchronized (this) {
445
                        destState = null;
446
                    }
447
                    //reschedule updating the final state
448
                    updateActiveStateTask.schedule(activationDelay);
449
                } else {
450
                    //add the becoming active state, remove the becoming inactive state
451
                    changeState(State.BECOMING_ACTIVE, State.BECOMING_INACTIVE);
452
                    //set our single destination state
453
                    synchronized (this) {
454
                        destState = State.ACTIVE;
455
                    }
456
                    //reschedule the task
457
                    updateActiveStateTask.schedule(activationDelay);
458
                }
459
            }
460
        }
461
462
        @Override
463
        public void run() {
464
            //Called when the state has been BECOMING_* for a sufficient
465
            //amount of time
466
            State dest;
467
            synchronized (this) {
468
                dest = this.destState;
469
            }
470
            if (dest == null) {
471
                changeState(null, State.BECOMING_INACTIVE, State.ACTIVE);
472
            } else {
473
                changeState(destState, State.BECOMING_ACTIVE);
474
            }
475
        }
476
    }
477
}
(-)a/openide.windows/src/org/openide/windows/ApplicationActivationManager.java (+190 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2011 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s): Tim Boudreau
39
 *
40
 * Portions Copyrighted 2011 Sun Microsystems, Inc.
41
 */
42
package org.openide.windows;
43
44
import java.awt.Window;
45
import java.util.EnumSet;
46
import java.util.Set;
47
import org.openide.util.Lookup;
48
49
/**
50
 * Lifecycle hook for determining whether the application is "active" (the user
51
 * is using it) or not, and whether or not it is "idle" (active but not in use
52
 * for a given period of time), and listening for changes in that state.
53
 * <p/>
54
 * Usage:  If you need to perform some work (such as checking for external 
55
 * changes) when the application becomes active, this class can help.  Another
56
 * case is if you have a long-running operation to perform (such as scanning
57
 * remote resources for changes) which must be done periodically, and you
58
 * do not want to interfere with normal operation - the idle state is useful
59
 * for this.
60
 * <p/>
61
 * The application achieves the "ACTIVE" state when it has been given focus
62
 * and not lost focus again for a sufficient amount of time;  it achieves the
63
 * "IDLE" state when it has focus but no user input has been received for
64
 * a sufficient amount of time.  By listening for changes in the ACTIVE state
65
 * you can perform work only when the application has really been reactivated,
66
 * avoiding notification because of very brief reactivation (for example, a 
67
 * user navigating between virtual desktops or applications).
68
 *
69
 * @author Tim Boudreau
70
 */
71
public abstract class ApplicationActivationManager {
72
73
    public static ApplicationActivationManager getDefault() {
74
        ApplicationActivationManager result = Lookup.getDefault().lookup(ApplicationActivationManager.class);
75
        if (result == null) {
76
            return new DummyImpl();
77
        }
78
        return result;
79
    }
80
81
    /**
82
     * Get the current set of states associated with the application.
83
     * 
84
     * @return A set of states, which may be empty if the application is
85
     * inactive
86
     */
87
    public abstract Set<State> getState();
88
89
    /**
90
     * Add a listener to be notified on changes in state.  The listener will
91
     * be notified on a background thread or the AWT event thread, depending
92
     * on the <code>notifyInForeground</code> parameter, when the application
93
     * changes state and the old or new set of states include a state in the
94
     * array of states passed to this method.
95
     * <p/>
96
     * The caller must hold a <i>strong reference</i> to the listener or it
97
     * may be immediately garbage collected.
98
     * 
99
     * @param listener The listener
100
     * @param notifyInForeground If true, the listener will be notified in the
101
     * AWT event thread;  if false, the listener will be notified on a background
102
     * thread.
103
     * @param interestedIn The set of states this listener is interested in.
104
     * May not be empty.
105
     */
106
    public abstract void addListener(Listener listener, boolean notifyInForeground, State... interestedIn);
107
108
    /**
109
     * Determine if the application is active.
110
     * 
111
     * @return Whether or not the application is active
112
     */
113
    public static boolean isActive() {
114
        return getDefault().getState().contains(State.ACTIVE);
115
    }
116
117
    /**
118
     * Determine if the application is currently idle (active but no keyboard
119
     * input or non-motion-related mouse input for a period of time).
120
     * @return Whether or not the application is idle
121
     */
122
    public static boolean isIdle() {
123
        return getDefault().getState().contains(State.IDLE);
124
    }
125
126
    /**
127
     * Callback which is notified when the application state changes
128
     */
129
    public interface Listener {
130
131
        /**
132
         * Called when the application state changes
133
         * @param from The old set of states
134
         * @param to The new set of states
135
         */
136
        public void onChange(Set<State> from, Set<State> to);
137
    }
138
139
    /**
140
     * States the application can have, relating to whether it is active
141
     * or idle.
142
     */
143
    public enum State {
144
145
        /**
146
         * The application is definitely active
147
         */
148
        ACTIVE,
149
        /**
150
         * The application has received focus, and if it still has focus after
151
         * a suitable delay, will be determined to be active.
152
         * <p/>
153
         * Note that this state can change very frequently, for example,
154
         * because a dialog was shown.
155
         */
156
        BECOMING_ACTIVE,
157
        /**
158
         * The application has lost focus, and if it still does not have focus
159
         * after a suitable delay, will be determined to be inactive.
160
         * <p/>
161
         * Note that this state can change very frequently, for example,
162
         * because a dialog was shown.
163
         */
164
        BECOMING_INACTIVE,
165
        /*
166
         * The application <i>is active</i> but no input events have been
167
         * received for longer than a suitable delay, so the application is
168
         * presumed to be idle (the user is not using the machine).
169
         */
170
        IDLE
171
    }
172
173
    private static final class DummyImpl extends ApplicationActivationManager {
174
175
        @Override
176
        public Set<State> getState() {
177
            for (Window w : Window.getWindows()) {
178
                if (w.isActive() && w.isFocusOwner()) {
179
                    return EnumSet.of(State.ACTIVE);
180
                }
181
            }
182
            return EnumSet.noneOf(State.class);
183
        }
184
185
        @Override
186
        public void addListener(Listener listener, boolean notifyInForeground, State... interestedIn) {
187
            //do nothing
188
        }
189
    }
190
}

Return to bug 194147