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 }