001    /*
002     * Created on Jan 13, 2009
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 @2009-2010 the original author or authors.
014     */
015    package org.fest.swing.core;
016    
017    import static javax.swing.SwingUtilities.convertRectangle;
018    import static org.fest.swing.edt.GuiActionRunner.execute;
019    
020    import java.awt.Component;
021    import java.awt.Container;
022    import java.awt.Rectangle;
023    
024    import javax.swing.JComponent;
025    import javax.swing.JInternalFrame;
026    import javax.swing.JViewport;
027    
028    import org.fest.swing.edt.GuiTask;
029    
030    /**
031     * Understands utility methods related to scrolling.
032     *
033     * @author Juhos Csaba-Zsolt
034     *
035     * @since 1.2
036     */
037    public final class Scrolling {
038    
039      /**
040       * Scrolls a <code>{@link JComponent}</code> into view within a container.
041       * @param robot simulates user input.
042       * @param c the given component.
043       */
044      public static void scrollToVisible(Robot robot, JComponent c) {
045        JComponent root = findClosestValidatingRootAncestor(c);
046        // scroll the component to view within each validating root ancestor, starting from the nearest
047        while (root != null) {
048          scrollToVisible(robot, root, c);
049          // find the next validating root
050          root = findClosestValidatingRootAncestor(root);
051        }
052      }
053    
054      /**
055       * Returns a component's closest validating root ancestor in the AWT containment hierarchy.
056       * @param c the given component.
057       * @return the found ancestor or <code>null</code> if there isn't one.
058       */
059      private static JComponent findClosestValidatingRootAncestor(JComponent c) {
060        // the candidate validating root at every iteration (candidate = not necessarily a root)
061        Container root = c;
062        // we go up to the top of the hierarchy
063        while (root != null) {
064          Container parent = root.getParent();
065          // the new candidate root becomes the parent of the previous one
066          root = parent;
067          // if the candidate isn't a JComponent, we're not interested in it (we need JComponent#scrollRectToVisible)
068          if (!(root instanceof JComponent)) continue;
069          // we don't have to take JFrame into account, it's not a JComponent (ant it's a top-level container anyway)
070          if (root instanceof JViewport || root instanceof JInternalFrame) return (JComponent) root;
071        }
072        return null;
073      }
074    
075      /**
076       * Scrolls a component into view within a container.
077       * @param robot simulates user input.
078       * @param container the given container.
079       * @param target the given component.
080       */
081      private static void scrollToVisible(Robot robot, JComponent container, Component target) {
082        Rectangle r = convertRectangle(target.getParent(), target.getBounds(), container);
083        scrollToVisible(robot, container, r);
084      }
085    
086      /**
087       * Scrolls a rectangular region of a component into view.
088       * @param robot simulates user input.
089       * @param c the component.
090       * @param rectangle the rectangular region.
091       */
092      private static void scrollToVisible(Robot robot, final JComponent c, final Rectangle rectangle) {
093        execute(new GuiTask() {
094          protected void executeInEDT() {
095            c.scrollRectToVisible(rectangle);
096          }
097        });
098        robot.waitForIdle();
099      }
100    
101      private Scrolling() {}
102    }