001    /*
002     * Created on Oct 8, 2007
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
005     * the License. You may obtain a copy of the License at
006     *
007     * http://www.apache.org/licenses/LICENSE-2.0
008     *
009     * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
010     * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
011     * specific language governing permissions and limitations under the License.
012     *
013     * Copyright @2007-2010 the original author or authors.
014     */
015    package org.fest.swing.monitor;
016    
017    import static org.fest.swing.edt.GuiActionRunner.execute;
018    
019    import java.awt.*;
020    import java.util.Collection;
021    
022    import org.fest.swing.annotation.RunsInCurrentThread;
023    import org.fest.swing.annotation.RunsInEDT;
024    import org.fest.swing.edt.GuiQuery;
025    import org.fest.util.VisibleForTesting;
026    
027    /**
028     * Understands a monitor that keeps track of all known root windows (showing, hidden, closed.)
029     *
030     * @author Alex Ruiz
031     */
032    public class WindowMonitor {
033    
034      private final Context context;
035      private final ContextMonitor contextMonitor;
036      private final Windows windows;
037      private final WindowStatus windowStatus;
038      private final WindowAvailabilityMonitor windowAvailabilityMonitor;
039    
040      /**
041       * Create an instance of WindowTracker which will track all windows coming and going on the current and subsequent
042       * <code>AppContext</code>s.
043       * <p>
044       * <strong>WARNING:</strong> if an applet loads this class, it will only ever see stuff in its own
045       * <code>AppContext</code>.
046       * </p>
047       * @param toolkit the <code>Toolkit</code> to use.
048       */
049      @RunsInCurrentThread
050      WindowMonitor(Toolkit toolkit) {
051        this(toolkit, new Context(toolkit), new WindowStatus(new Windows()));
052      }
053    
054      @VisibleForTesting
055      @RunsInCurrentThread
056      WindowMonitor(Toolkit toolkit, Context context, WindowStatus windowStatus) {
057        this.context = context;
058        this.windowStatus = windowStatus;
059        windows = windowStatus.windows();
060        contextMonitor = new ContextMonitor(context, windows);
061        contextMonitor.attachTo(toolkit);
062        windowAvailabilityMonitor = new WindowAvailabilityMonitor(windows);
063        windowAvailabilityMonitor.attachTo(toolkit);
064        populateExistingWindows();
065      }
066    
067      private void populateExistingWindows() {
068        for (Frame f : Frame.getFrames()) examine(f);
069      }
070    
071      @RunsInCurrentThread
072      private void examine(Window w) {
073        windows.attachNewWindowVisibilityMonitor(w);
074        for (Window owned : w.getOwnedWindows()) examine(owned);
075        windows.markExisting(w);
076        context.addContextFor(w);
077      }
078    
079      /**
080       * Returns whether the window is ready to receive OS-level event input. A window's "isShowing" flag may be set
081       * <code>true</code> before the <code>WINDOW_OPENED</code> event is generated, and even after the
082       * <code>WINDOW_OPENED</code> is sent the window peer is not guaranteed to be ready.
083       * @param w the given window.
084       * @return whether the window is ready to receive OS-level event input.
085       */
086      public boolean isWindowReady(Window w) {
087        if (windows.isReady(w)) return true;
088        windowStatus.checkIfReady(w);
089        return false;
090      }
091    
092      /**
093       * Returns the event queue corresponding to the given component. In most cases, this is the same as
094       * <code>Component.getToolkit().getSystemEventQueue()</code>, but in the case of applets will bypass the
095       * <code>AppContext</code> and provide the real event queue.
096       * @param c the given component.
097       * @return the event queue corresponding to the given component.
098       */
099      public EventQueue eventQueueFor(Component c) {
100        return context.eventQueueFor(c);
101      }
102    
103      /**
104       * Returns all known event queues.
105       * @return all known event queues.
106       */
107      public Collection<EventQueue> allEventQueues() {
108        return context.allEventQueues();
109      }
110    
111      /**
112       * Return all available root windows. A root window is one that has a null parent. Nominally this means a list similar
113       * to that returned by <code>{@link Frame#getFrames() Frame.getFrames()}</code>, but in the case of an
114       * <code>{@link java.applet.Applet}</code> may return a few dialogs as well.
115       * @return all available root windows.
116       */
117      public Collection<Window> rootWindows() {
118        return context.rootWindows();
119      }
120    
121      /**
122       * Returns the singleton instance of this class.
123       * @return the singleton instance of this class.
124       */
125      @RunsInEDT
126      public static WindowMonitor instance() {
127        return SingletonLazyLoader.INSTANCE;
128      }
129    
130      @RunsInEDT
131      private static class SingletonLazyLoader {
132        static final WindowMonitor INSTANCE = execute(new GuiQuery<WindowMonitor>() {
133          protected WindowMonitor executeInEDT() throws Throwable {
134            return new WindowMonitor(Toolkit.getDefaultToolkit());
135          }
136        });
137      }
138    }