001 /* 002 * Created on Jan 27, 2008 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 @2008-2010 the original author or authors. 014 */ 015 package org.fest.swing.driver; 016 017 import static org.fest.swing.driver.ComponentMovableQuery.isUserMovable; 018 import static org.fest.swing.driver.ComponentMoveTask.moveComponent; 019 import static org.fest.swing.driver.ComponentSetSizeTask.setComponentSize; 020 import static org.fest.swing.driver.ComponentStateValidator.componentNotShowingOnScreenFailure; 021 import static org.fest.swing.driver.ComponentStateValidator.validateIsEnabledAndShowing; 022 import static org.fest.swing.driver.ContainerStateValidator.validateCanResize; 023 import static org.fest.swing.edt.GuiActionRunner.execute; 024 import static org.fest.swing.format.Formatting.format; 025 import static org.fest.util.Strings.concat; 026 027 import java.awt.*; 028 029 import org.fest.swing.annotation.RunsInCurrentThread; 030 import org.fest.swing.annotation.RunsInEDT; 031 import org.fest.swing.core.Robot; 032 import org.fest.swing.edt.GuiQuery; 033 import org.fest.swing.util.Pair; 034 import org.fest.swing.util.Triple; 035 036 /** 037 * Understands functional testing of <code>{@link Container}</code>s: 038 * <ul> 039 * <li>user input simulation</li> 040 * <li>state verification</li> 041 * <li>property value query</li> 042 * </ul> 043 * This class is intended for internal use only. Please use the classes in the package 044 * <code>{@link org.fest.swing.fixture}</code> in your tests. 045 * 046 * @author Alex Ruiz 047 * @author Yvonne Wang 048 */ 049 public abstract class ContainerDriver extends ComponentDriver { 050 051 /** 052 * Creates a new </code>{@link ContainerDriver}</code>. 053 * @param robot the robot to use to simulate user input. 054 */ 055 public ContainerDriver(Robot robot) { 056 super(robot); 057 } 058 059 /** 060 * Resizes the <code>{@link Container}</code> horizontally. 061 * @param c the target <code>Container</code>. 062 * @param width the width that the <code>Container</code> should have after being resized. 063 * @throws IllegalStateException if the <code>Container</code> is not enabled. 064 * @throws IllegalStateException if the <code>Container</code> is not resizable by the user. 065 * @throws IllegalStateException if the <code>Container</code> is not showing on the screen. 066 */ 067 @RunsInEDT 068 protected final void resizeWidth(Container c, int width) { 069 Pair<Dimension, Insets> resizeInfo = resizeInfo(c); 070 Dimension size = resizeInfo.i; 071 resizeBy(c, resizeInfo, width - size.width, 0); 072 } 073 074 /** 075 * Resizes the <code>{@link Container}</code> vertically. 076 * @param c the target <code>Container</code>. 077 * @param height the height that the <code>Container</code> should have after being resized. 078 * @throws IllegalStateException if the <code>Container</code> is not enabled. 079 * @throws IllegalStateException if the <code>Container</code> is not resizable by the user. 080 * @throws IllegalStateException if the <code>Container</code> is not showing on the screen. 081 */ 082 @RunsInEDT 083 protected final void resizeHeight(Container c, int height) { 084 Pair<Dimension, Insets> resizeInfo = resizeInfo(c); 085 Dimension size = resizeInfo.i; 086 resizeBy(c, resizeInfo, 0, height - size.height); 087 } 088 089 /** 090 * Resizes the <code>{@link Container}</code> to the given size. 091 * @param c the target <code>Container</code>. 092 * @param width the width to resize the <code>Container</code> to. 093 * @param height the height to resize the <code>Container</code> to. 094 * @throws IllegalStateException if the <code>Container</code> is not enabled. 095 * @throws IllegalStateException if the <code>Container</code> is not resizable by the user. 096 * @throws IllegalStateException if the <code>Container</code> is not showing on the screen. 097 */ 098 @RunsInEDT 099 protected final void resize(Container c, int width, int height) { 100 Pair<Dimension, Insets> resizeInfo = resizeInfo(c); 101 Dimension size = resizeInfo.i; 102 resizeBy(c, resizeInfo, width - size.width, height - size.height); 103 } 104 105 @RunsInEDT 106 private static Pair<Dimension, Insets> resizeInfo(final Container c) { 107 return execute(new GuiQuery<Pair<Dimension, Insets>>() { 108 protected Pair<Dimension, Insets> executeInEDT() { 109 validateCanResize(c); 110 return new Pair<Dimension, Insets>(c.getSize(), c.getInsets()); 111 } 112 }); 113 } 114 115 @RunsInEDT 116 private void resizeBy(Container c, Pair<Dimension, Insets> resizeInfo, int x, int y) { 117 simulateResizeStarted(c, resizeInfo, x, y); 118 Dimension size = resizeInfo.i; 119 setComponentSize(c, size.width + x, size.height + y); 120 robot.waitForIdle(); 121 } 122 123 @RunsInEDT 124 private void simulateResizeStarted(Container c, Pair<Dimension, Insets> resizeInfo, int x, int y) { 125 Point p = resizeLocation(resizeInfo); 126 moveMouseIgnoringAnyError(c, p); 127 moveMouseIgnoringAnyError(c, p.x + x, p.y + y); 128 } 129 130 private static Point resizeLocation(final Pair<Dimension, Insets> resizeInfo) { 131 return resizeLocation(resizeInfo.i, resizeInfo.ii); 132 } 133 134 private static Point resizeLocation(Dimension size, Insets insets) { 135 return resizeLocation(size.width, size.height, insets.right, insets.bottom); 136 } 137 138 private static Point resizeLocation(int width, int height, int right, int bottom) { 139 return new Point(width - right / 2, height - bottom / 2); 140 } 141 142 /** 143 * Move the given <code>{@link Container}</code> to the requested location. 144 * @param c the target <code>Container</code>. 145 * @param x the horizontal coordinate. 146 * @param y the vertical coordinate. 147 * @throws IllegalStateException if the <code>Container</code> is not enabled. 148 * @throws IllegalStateException if the <code>Container</code> is not movable by the user. 149 * @throws IllegalStateException if the <code>Container</code> is not showing on the screen. 150 */ 151 @RunsInEDT 152 public void move(Container c, int x, int y) { 153 Triple<Dimension, Insets, Point> moveInfo = moveInfo(c); 154 Point locationOnScreen = moveInfo.iii; 155 moveBy(c, moveInfo, x - locationOnScreen.x, y - locationOnScreen.y); 156 } 157 158 @RunsInEDT 159 private static Triple<Dimension, Insets, Point> moveInfo(final Container c) { 160 return execute(new GuiQuery<Triple<Dimension, Insets, Point>>() { 161 protected Triple<Dimension, Insets, Point> executeInEDT() { 162 validateCanMove(c); 163 Point locationOnScreen = null; 164 try { 165 locationOnScreen = c.getLocationOnScreen(); 166 } catch (IllegalComponentStateException e) { 167 // we should not get to this point, validateIsShowing should have already catched that the container is not 168 // visible. 169 } 170 if (locationOnScreen == null) throw componentNotShowingOnScreenFailure(c); 171 return new Triple<Dimension, Insets, Point>(c.getSize(), c.getInsets(), locationOnScreen) ; 172 } 173 }); 174 } 175 176 @RunsInCurrentThread 177 private static void validateCanMove(Container c) { 178 validateIsEnabledAndShowing(c); 179 if (!isUserMovable(c)) 180 throw new IllegalStateException(concat("Expecting component ", format(c), " to be movable by the user")); 181 } 182 183 @RunsInEDT 184 private void moveBy(Container c, Triple<Dimension, Insets, Point> moveInfo, int x, int y) { 185 simulateMoveStarted(c, moveInfo, x, y); 186 Point locationOnScreen = moveInfo.iii; 187 Point location = new Point(locationOnScreen.x + x, locationOnScreen.y + y); 188 moveComponent(c, location); 189 robot.waitForIdle(); 190 } 191 192 @RunsInEDT 193 private void simulateMoveStarted(Container c, Triple<Dimension, Insets, Point> moveInfo, int x, int y) { 194 Point p = moveLocation(moveInfo.i, moveInfo.ii); 195 moveMouseIgnoringAnyError(c, p); 196 moveMouseIgnoringAnyError(c, p.x + x, p.y + y); 197 } 198 199 // Returns where the mouse usually grabs to move a container (or window.) Center of the top of the frame is usually a 200 // good choice. 201 private Point moveLocation(Dimension size, Insets insets) { 202 return new Point(size.width / 2, insets.top / 2); 203 } 204 }