001 /*
002 * Created on Feb 1, 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.ComponentStateValidator.validateIsShowing;
018 import static org.fest.swing.driver.JInternalFrameAction.*;
019 import static org.fest.swing.driver.JInternalFrameIconQuery.isIconified;
020 import static org.fest.swing.driver.JInternalFrameSetIconTask.setIcon;
021 import static org.fest.swing.driver.JInternalFrameSetMaximumTask.setMaximum;
022 import static org.fest.swing.driver.WindowLikeContainerLocations.*;
023 import static org.fest.swing.edt.GuiActionRunner.execute;
024 import static org.fest.swing.exception.ActionFailedException.actionFailure;
025 import static org.fest.swing.format.Formatting.format;
026 import static org.fest.util.Strings.concat;
027
028 import java.awt.*;
029 import java.beans.PropertyVetoException;
030
031 import javax.swing.JInternalFrame;
032 import javax.swing.JInternalFrame.JDesktopIcon;
033
034 import org.fest.swing.annotation.RunsInCurrentThread;
035 import org.fest.swing.annotation.RunsInEDT;
036 import org.fest.swing.core.Robot;
037 import org.fest.swing.edt.GuiQuery;
038 import org.fest.swing.edt.GuiTask;
039 import org.fest.swing.exception.ActionFailedException;
040 import org.fest.swing.exception.UnexpectedException;
041 import org.fest.swing.util.Pair;
042 import org.fest.swing.util.Triple;
043 import org.fest.util.VisibleForTesting;
044
045 /**
046 * Understands functional testing of <code>{@link JInternalFrame}</code>s:
047 * <ul>
048 * <li>user input simulation</li>
049 * <li>state verification</li>
050 * <li>property value query</li>
051 * </ul>
052 * This class is intended for internal use only. Please use the classes in the package
053 * <code>{@link org.fest.swing.fixture}</code> in your tests.
054 *
055 * @author Alex Ruiz
056 * @author Yvonne Wang
057 */
058 public class JInternalFrameDriver extends JComponentDriver {
059
060 /**
061 * Creates a new </code>{@link JInternalFrameDriver}</code>.
062 * @param robot the robot to use to simulate user input.
063 */
064 public JInternalFrameDriver(Robot robot) {
065 super(robot);
066 }
067
068 /**
069 * Brings the given <code>{@link JInternalFrame}</code> to the front.
070 * @param internalFrame the target <code>JInternalFrame</code>.
071 */
072 @RunsInEDT
073 public void moveToFront(JInternalFrame internalFrame) {
074 toFront(internalFrame);
075 }
076
077 @RunsInEDT
078 private static void toFront(final JInternalFrame internalFrame) {
079 execute(new GuiTask() {
080 protected void executeInEDT() {
081 // it seems that moving to front always works, regardless if the internal frame is invisible and/or disabled.
082 internalFrame.toFront();
083 }
084 });
085 }
086
087 /**
088 * Brings the given <code>{@link JInternalFrame}</code> to the back.
089 * @param internalFrame the target <code>JInternalFrame</code>.
090 */
091 @RunsInEDT
092 public void moveToBack(JInternalFrame internalFrame) {
093 toBack(internalFrame);
094 }
095
096 @RunsInEDT
097 private static void toBack(final JInternalFrame internalFrame) {
098 execute(new GuiTask() {
099 protected void executeInEDT() {
100 // it seems that moving to back always works, regardless if the internal frame is invisible and/or disabled.
101 internalFrame.moveToBack();
102 }
103 });
104 }
105
106 /**
107 * Maximizes the given <code>{@link JInternalFrame}</code>, deconifying it first if it is iconified.
108 * @param internalFrame the target <code>JInternalFrame</code>.
109 * @throws IllegalStateException if the <code>JInternalFrame</code> is not maximizable.
110 * @throws IllegalStateException if the <code>JInternalFrame</code> is not showing on the screen.
111 * @throws ActionFailedException if the <code>JInternalFrame</code> vetoes the action.
112 */
113 @RunsInEDT
114 public void maximize(JInternalFrame internalFrame) {
115 Pair<Container, Point> maximizeLocation = validateAndFindMaximizeLocation(internalFrame);
116 maximizeOrNormalize(internalFrame, MAXIMIZE, maximizeLocation);
117 }
118
119 @RunsInEDT
120 private static Pair<Container, Point> validateAndFindMaximizeLocation(final JInternalFrame internalFrame) {
121 return execute(new GuiQuery<Pair<Container, Point>>() {
122 protected Pair<Container, Point> executeInEDT() {
123 validateCanMaximize(internalFrame);
124 return findMaximizeLocation(internalFrame);
125 }
126 });
127 }
128
129 @RunsInCurrentThread
130 private static void validateCanMaximize(JInternalFrame internalFrame) {
131 validateIsShowingOrIconified(internalFrame);
132 if (!internalFrame.isMaximizable())
133 throw new IllegalStateException(concat("The JInternalFrame <", format(internalFrame), "> is not maximizable"));
134 }
135
136 /**
137 * Normalizes the given <code>{@link JInternalFrame}</code>, deconifying it first if it is iconified.
138 * @param internalFrame the target <code>JInternalFrame</code>.
139 * @throws IllegalStateException if the <code>JInternalFrame</code> is not showing on the screen.
140 * @throws ActionFailedException if the <code>JInternalFrame</code> vetoes the action.
141 */
142 @RunsInEDT
143 public void normalize(JInternalFrame internalFrame) {
144 Pair<Container, Point> normalizeLocation = validateAndFindNormalizeLocation(internalFrame);
145 maximizeOrNormalize(internalFrame, NORMALIZE, normalizeLocation);
146 }
147
148 @RunsInEDT
149 private static Pair<Container, Point> validateAndFindNormalizeLocation(final JInternalFrame internalFrame) {
150 return execute(new GuiQuery<Pair<Container, Point>>() {
151 protected Pair<Container, Point> executeInEDT() {
152 validateIsShowingOrIconified(internalFrame);
153 return findMaximizeLocation(internalFrame);
154 }
155 });
156 }
157
158 @RunsInCurrentThread
159 private static void validateIsShowingOrIconified(JInternalFrame internalFrame) {
160 if (!internalFrame.isIcon()) validateIsShowing(internalFrame);
161 }
162
163 @RunsInCurrentThread
164 private static Pair<Container, Point> findMaximizeLocation(JInternalFrame internalFrame) {
165 Container clickTarget = internalFrame.isIcon() ? internalFrame.getDesktopIcon() : internalFrame;
166 Point location = maximizeLocationOf(clickTarget);
167 return new Pair<Container, Point>(clickTarget, location);
168 }
169
170 @RunsInEDT
171 private void maximizeOrNormalize(JInternalFrame internalFrame, JInternalFrameAction action,
172 Pair<Container, Point> toMoveMouseTo) {
173 moveMouseIgnoringAnyError(toMoveMouseTo.i, toMoveMouseTo.ii);
174 setMaximumProperty(internalFrame, action);
175 }
176
177 @RunsInEDT
178 private void setMaximumProperty(JInternalFrame internalFrame, JInternalFrameAction action) {
179 try {
180 setMaximum(internalFrame, action);
181 robot.waitForIdle();
182 } catch (UnexpectedException unexpected) {
183 failIfVetoed(internalFrame, action, unexpected);
184 }
185 }
186
187 /**
188 * Iconifies the given <code>{@link JInternalFrame}</code>.
189 * @param internalFrame the target <code>JInternalFrame</code>.
190 * @throws IllegalStateException if the <code>JInternalFrame</code> is not showing on the screen.
191 * @throws IllegalStateException if the <code>JInternalFrame</code> is not iconifiable.
192 * @throws ActionFailedException if the <code>JInternalFrame</code> vetoes the action.
193 */
194 @RunsInEDT
195 public void iconify(JInternalFrame internalFrame) {
196 Pair<Boolean, Point> iconifyInfo = validateAndfindIconifyInfo(internalFrame);
197 if (iconifyInfo.i) return; // internal frame is already iconified
198 moveMouseIgnoringAnyError(internalFrame, iconifyInfo.ii);
199 setIconProperty(internalFrame, ICONIFY);
200 }
201
202 @RunsInEDT
203 private static Pair<Boolean, Point> validateAndfindIconifyInfo(final JInternalFrame internalFrame) {
204 return execute(new GuiQuery<Pair<Boolean, Point>>() {
205 protected Pair<Boolean, Point> executeInEDT() throws Throwable {
206 validateIsShowingOrIconified(internalFrame);
207 if (!internalFrame.isIconifiable())
208 throw new IllegalStateException(concat("The JInternalFrame <", format(internalFrame), "> is not iconifiable"));
209 return iconifyInfo(internalFrame);
210 }
211 });
212 }
213
214 @RunsInCurrentThread
215 private static Pair<Boolean, Point> iconifyInfo(JInternalFrame internalFrame) {
216 boolean iconified = isIconified(internalFrame);
217 if (iconified) return new Pair<Boolean, Point>(true, null);
218 return new Pair<Boolean, Point>(iconified, findIconifyLocation(internalFrame));
219 }
220
221 /**
222 * De-iconifies the given <code>{@link JInternalFrame}</code>.
223 * @param internalFrame the target <code>JInternalFrame</code>.
224 * @throws IllegalStateException if the <code>JInternalFrame</code> is not showing on the screen.
225 * @throws ActionFailedException if the <code>JInternalFrame</code> vetoes the action.
226 */
227 @RunsInEDT
228 public void deiconify(JInternalFrame internalFrame) {
229 Triple<Boolean, Container, Point> deiconifyInfo = validateAndfindDeiconifyInfo(internalFrame);
230 if (deiconifyInfo.i) return; // internal frame is already de-iconified
231 moveMouseIgnoringAnyError(deiconifyInfo.ii, deiconifyInfo.iii);
232 setIconProperty(internalFrame, DEICONIFY);
233 }
234
235 @RunsInEDT
236 private static Triple<Boolean, Container, Point> validateAndfindDeiconifyInfo(final JInternalFrame internalFrame) {
237 return execute(new GuiQuery<Triple<Boolean, Container, Point>>() {
238 protected Triple<Boolean, Container, Point> executeInEDT() throws Throwable {
239 validateIsShowingOrIconified(internalFrame);
240 return deiconifyInfo(internalFrame);
241 }
242 });
243 }
244
245 @RunsInCurrentThread
246 private static Triple<Boolean, Container, Point> deiconifyInfo(JInternalFrame internalFrame) {
247 boolean deiconified = !isIconified(internalFrame);
248 if (deiconified) return new Triple<Boolean, Container, Point>(true, null, null);
249 JDesktopIcon desktopIcon = internalFrame.getDesktopIcon();
250 return new Triple<Boolean, Container, Point>(deiconified, desktopIcon, iconifyLocationOf(desktopIcon));
251 }
252
253 @RunsInCurrentThread
254 private static Point findIconifyLocation(JInternalFrame internalFrame) {
255 return iconifyLocationOf(internalFrame.getDesktopIcon());
256 }
257
258 @RunsInEDT
259 private void setIconProperty(JInternalFrame internalFrame, JInternalFrameAction action) {
260 try {
261 setIcon(internalFrame, action);
262 robot.waitForIdle();
263 } catch (UnexpectedException unexpected) {
264 failIfVetoed(internalFrame, action, unexpected);
265 }
266 }
267
268 @VisibleForTesting
269 void failIfVetoed(JInternalFrame internalFrame, JInternalFrameAction action, UnexpectedException unexpected) {
270 PropertyVetoException vetoError = vetoFrom(unexpected);
271 if (vetoError == null) return;
272 throw actionFailure(concat(action.name, " of ", format(internalFrame), " was vetoed: <", vetoError.getMessage(), ">"));
273 }
274
275 private PropertyVetoException vetoFrom(UnexpectedException unexpected) {
276 Throwable cause = unexpected.getCause();
277 if (!(cause instanceof PropertyVetoException)) return null;
278 return (PropertyVetoException)cause;
279 }
280
281 /**
282 * Resizes the <code>{@link JInternalFrame}</code> horizontally.
283 * @param internalFrame the target <code>JInternalFrame</code>.
284 * @param width the width that the <code>JInternalFrame</code> should have after being resized.
285 * @throws IllegalStateException if the <code>JInternalFrame</code> is not showing on the screen.
286 * @throws IllegalStateException if the <code>JInternalFrame</code> is not resizable by the user.
287 */
288 @RunsInEDT
289 public void resizeWidthTo(JInternalFrame internalFrame, int width) {
290 resizeWidth(internalFrame, width);
291 }
292
293 /**
294 * Resizes the <code>{@link JInternalFrame}</code> vertically.
295 * @param w the target <code>JInternalFrame</code>.
296 * @param height the height that the <code>JInternalFrame</code> should have after being resized.
297 * @throws IllegalStateException if the <code>JInternalFrame</code> is not showing on the screen.
298 * @throws IllegalStateException if the <code>JInternalFrame</code> is not resizable by the user.
299 */
300 @RunsInEDT
301 public void resizeHeightTo(JInternalFrame w, int height) {
302 resizeHeight(w, height);
303 }
304
305 /**
306 * Resizes the <code>{@link JInternalFrame}</code> to the given size.
307 * @param internalFrame the target <code>JInternalFrame</code>.
308 * @param size the size to resize the <code>JInternalFrame</code> to.
309 * @throws IllegalStateException if the <code>JInternalFrame</code> is not showing on the screen.
310 * @throws IllegalStateException if the <code>JInternalFrame</code> is not resizable by the user.
311 */
312 @RunsInEDT
313 public void resizeTo(JInternalFrame internalFrame, Dimension size) {
314 resize(internalFrame, size.width, size.height);
315 }
316
317 /**
318 * Moves the <code>{@link JInternalFrame}</code> to the given location.
319 * @param internalFrame the target <code>JInternalFrame</code>.
320 * @param where the location to move the <code>JInternalFrame</code> to.
321 * @throws IllegalStateException if the <code>JInternalFrame</code> is not showing on the screen.
322 */
323 @RunsInEDT
324 public void moveTo(JInternalFrame internalFrame, Point where) {
325 move(internalFrame, where.x, where.y);
326 }
327
328 /**
329 * Closes the given <code>{@link JInternalFrame}</code>.
330 * @param internalFrame the target <code>JInternalFrame</code>.
331 * @throws IllegalStateException if the <code>JInternalFrame</code> is not showing on the screen.
332 * @throws IllegalStateException if the <code>JInternalFrame</code> is not closable.
333 */
334 @RunsInEDT
335 public void close(JInternalFrame internalFrame) {
336 Pair<Boolean, Point> closeInfo = validateAndFindCloseInfo(internalFrame);
337 if (closeInfo.i) return; // internal frame is already closed
338 moveMouseIgnoringAnyError(internalFrame, closeInfo.ii);
339 JInternalFrameCloseTask.close(internalFrame);
340 robot.waitForIdle();
341 }
342
343 @RunsInEDT
344 private static Pair<Boolean, Point> validateAndFindCloseInfo(final JInternalFrame internalFrame) {
345 return execute(new GuiQuery<Pair<Boolean, Point>>() {
346 protected Pair<Boolean, Point> executeInEDT() {
347 validateCanClose(internalFrame);
348 return closeInfo(internalFrame);
349 }
350 });
351 }
352
353 @RunsInCurrentThread
354 private static void validateCanClose(JInternalFrame internalFrame) {
355 validateIsShowing(internalFrame);
356 if (!internalFrame.isClosable())
357 throw new IllegalStateException(concat("The JInternalFrame <", format(internalFrame), "> is not closable"));
358 }
359
360 @RunsInCurrentThread
361 private static Pair<Boolean, Point> closeInfo(JInternalFrame internalFrame) {
362 if (internalFrame.isClosed()) return new Pair<Boolean, Point>(true, null);
363 return new Pair<Boolean, Point>(false, closeLocationOf(internalFrame));
364 }
365
366 }