/** * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. * * Copyright 2012-2015 the original author or authors. */ package org.assertj.swing.monitor; import static org.assertj.core.util.Preconditions.checkNotNull; import static org.assertj.swing.edt.GuiActionRunner.execute; import java.awt.Component; import java.awt.EventQueue; import java.awt.Frame; import java.awt.Toolkit; import java.awt.Window; import java.util.Collection; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.assertj.core.util.VisibleForTesting; import org.assertj.swing.annotation.RunsInCurrentThread; import org.assertj.swing.annotation.RunsInEDT; import org.assertj.swing.edt.GuiQuery; import org.assertj.swing.util.ToolkitProvider; /** * Monitor that keeps track of all known root AWT or Swing {@code Window}s (showing, hidden, closed). * * @author Alex Ruiz */ public class WindowMonitor { private final Context context; private final ContextMonitor contextMonitor; private final Windows windows; private final WindowStatus windowStatus; private final WindowAvailabilityMonitor windowAvailabilityMonitor; /** * <p> * Create an instance of WindowTracker which will track all windows coming and going on the current and subsequent * {@code AppContext}s. * </p> * * <p> * <strong>WARNING:</strong> if an applet loads this class, it will only ever see stuff in its own {@code AppContext}. * </p> * * @param toolkit the {@code Toolkit} to use. */ @RunsInCurrentThread WindowMonitor(@Nonnull Toolkit toolkit) { this(toolkit, new Context(toolkit), new WindowStatus(new Windows())); } @VisibleForTesting @RunsInCurrentThread WindowMonitor(@Nonnull Toolkit toolkit, @Nonnull Context context, @Nonnull WindowStatus windowStatus) { this.context = context; this.windowStatus = windowStatus; windows = windowStatus.windows(); contextMonitor = new ContextMonitor(context, windows); contextMonitor.attachTo(toolkit); windowAvailabilityMonitor = new WindowAvailabilityMonitor(windows); windowAvailabilityMonitor.attachTo(toolkit); populateExistingWindows(); } private void populateExistingWindows() { for (Frame f : Frame.getFrames()) { examine(checkNotNull(f)); } } @RunsInCurrentThread private void examine(@Nonnull Window w) { windows.attachNewWindowVisibilityMonitor(w); for (Window owned : w.getOwnedWindows()) { examine(checkNotNull(owned)); } windows.markExisting(w); context.addContextFor(w); } /** * Returns whether the AWT or Swing {@code Window} is ready to receive OS-level event input. A {@code Window}'s * "isShowing" flag may be set {@code true} before the {@code WINDOW_OPENED} event is generated, and even after the * {@code WINDOW_OPENED} event is sent, the {@code Window} peer is not guaranteed to be ready. * * @param w the given {@code Window}. * @return whether the {@code Window} is ready to receive OS-level event input. */ public boolean isWindowReady(@Nonnull Window w) { if (windows.isReady(w)) { return true; } windowStatus.checkIfReady(w); return false; } /** * Returns the event queue corresponding to the given AWT or Swing {@code Compoenent}. In most cases, this is the same * as {@code Component.getToolkit().getSystemEventQueue()}, but in the case of applets will bypass the * {@code AppContext} and provide the real event queue. * * @param c the given {@code Component}. * @return the event queue corresponding to the given component. */ public @Nullable EventQueue eventQueueFor(@Nonnull Component c) { return context.eventQueueFor(c); } /** * @return all known event queues. */ public @Nonnull Collection<EventQueue> allEventQueues() { return context.allEventQueues(); } /** * Return all available root {@code Window}s. A root {@code Window} is one that has a {@code null} parent. Nominally * this means a list similar to that returned by {@code Frame.getFrames()}, but in the case of an {@code Applet} may * return a few dialogs as well. * * @return all available root {@code Window}s. */ public @Nonnull Collection<Window> rootWindows() { return context.rootWindows(); } /** * @return the singleton instance of this class. */ @RunsInEDT public static @Nonnull WindowMonitor instance() { return SingletonLazyLoader.INSTANCE; } @RunsInEDT private static class SingletonLazyLoader { static final WindowMonitor INSTANCE = execute(new GuiQuery<WindowMonitor>() { @Override protected WindowMonitor executeInEDT() { return new WindowMonitor(ToolkitProvider.instance().defaultToolkit()); }; }); } }