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 }