001    /*
002     * Created on Nov 21, 2008
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
005     * in compliance with 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
010     * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
011     * or implied. See the License for the specific language governing permissions and limitations under
012     * the License.
013     * 
014     * Copyright @2008-2010 the original author or authors.
015     */
016    package org.fest.swing.driver;
017    
018    import static org.fest.swing.query.ComponentVisibleQuery.isVisible;
019    import static org.fest.swing.timing.Pause.pause;
020    import static org.fest.swing.util.TimeoutWatch.startWatchWithTimeoutOf;
021    
022    import java.awt.Component;
023    import java.awt.event.ComponentAdapter;
024    import java.awt.event.ComponentEvent;
025    
026    import org.fest.swing.annotation.RunsInEDT;
027    import org.fest.swing.exception.WaitTimedOutError;
028    import org.fest.swing.util.TimeoutWatch;
029    
030    /**
031     * Understands waiting for a <code>{@link Component}</code> to be shown.
032     *
033     * @author Alex Ruiz
034     */
035    public final class ComponentShownWaiter extends ComponentAdapter {
036    
037      private static final int DEFAULT_TIMEOUT = 5000;
038      private static final int DEFAULT_SLEEP_TIME = 10;
039      
040      private Component toWaitFor;
041      private volatile boolean shown;
042      
043      /**
044       * Waits until the given component is shown on the screen, using a timeout of 5 seconds.
045       * @param toWaitFor the component to wait for.
046       * @throws WaitTimedOutError if the component is not shown before the default timeout of 5 seconds.
047       */
048      public static void waitTillShown(Component toWaitFor) {
049        new ComponentShownWaiter(toWaitFor).startWaiting(DEFAULT_TIMEOUT);
050      }
051    
052      /**
053       * Waits until the given component is shown on the screen.
054       * @param toWaitFor the component to wait for.
055       * @param timeout the amount to time (in milliseconds) to wait for the component to be shown. 
056       * @throws WaitTimedOutError if the component is not shown before the given timeout expires.
057       */
058      public static void waitTillShown(Component toWaitFor, long timeout) {
059        new ComponentShownWaiter(toWaitFor).startWaiting(timeout);
060      }
061      
062      private ComponentShownWaiter(Component toWaitFor) {
063        this.toWaitFor = toWaitFor;
064        toWaitFor.addComponentListener(this);
065      }
066    
067      private void startWaiting(long timeout) {
068        if (alreadyVisible()) return;
069        TimeoutWatch watch = startWatchWithTimeoutOf(timeout);
070        while (!shown) {
071          pause(DEFAULT_SLEEP_TIME);
072          if (watch.isTimeOut()) {
073            done();
074            throw new WaitTimedOutError("Timed out waiting for component to be visible");
075          }
076        }
077      }
078    
079      private boolean alreadyVisible() {
080        if (!isVisible(toWaitFor)) return false; 
081        done();
082        return true;
083      }
084      
085      /**
086       * Notification that the component to wait for is finally shown on the screen.
087       * @param e the event raised when the component has been made visible.
088       */
089      @RunsInEDT
090      @Override public void componentShown(ComponentEvent e) {
091        shown = true;
092        done();
093      }
094    
095      private void done() {
096        toWaitFor.removeComponentListener(this);
097        toWaitFor = null;
098      }
099    }