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 }