001    /*
002     * Created on Feb 2, 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 javax.swing.SwingUtilities.getWindowAncestor;
018    import static org.fest.reflect.core.Reflection.field;
019    import static org.fest.swing.driver.ComponentStateValidator.validateIsEnabledAndShowing;
020    import static org.fest.swing.driver.JToolBarIsFloatingQuery.isJToolBarFloating;
021    import static org.fest.swing.edt.GuiActionRunner.execute;
022    import static org.fest.swing.exception.ActionFailedException.actionFailure;
023    import static org.fest.swing.format.Formatting.format;
024    import static org.fest.util.Strings.concat;
025    import static org.fest.util.Strings.quote;
026    
027    import java.awt.*;
028    
029    import javax.swing.JToolBar;
030    
031    import org.fest.swing.annotation.RunsInCurrentThread;
032    import org.fest.swing.annotation.RunsInEDT;
033    import org.fest.swing.core.Robot;
034    import org.fest.swing.edt.GuiQuery;
035    import org.fest.swing.edt.GuiTask;
036    import org.fest.swing.exception.ActionFailedException;
037    import org.fest.swing.util.GenericRange;
038    import org.fest.swing.util.Pair;
039    
040    /**
041     * Understands functional testing of <code>{@link JToolBar}</code>s:
042     * <ul>
043     * <li>user input simulation</li>
044     * <li>state verification</li>
045     * <li>property value query</li>
046     * </ul>
047     * This class is intended for internal use only. Please use the classes in the package
048     * <code>{@link org.fest.swing.fixture}</code> in your tests.
049     *
050     * @author Yvonne Wang
051     * @author Alex Ruiz
052     */
053    public class JToolBarDriver extends JComponentDriver {
054    
055      private final JToolBarLocation location = new JToolBarLocation();
056    
057      /**
058       * Creates a new </code>{@link JToolBarDriver}</code>.
059       * @param robot the robot to use to simulate user input.
060       */
061      public JToolBarDriver(Robot robot) {
062        super(robot);
063      }
064    
065      /**
066       * Indicates whether the given <code>{@link JToolBar}</code> is floating or not.
067       * @param toolBar the target <code>JToolBar</code>.
068       * @return <code>true</code> if the <code>JToolBar</code> is floating, <code>false</code> otherwise.
069       */
070      @RunsInEDT
071      public boolean isFloating(JToolBar toolBar) {
072        return floating(toolBar);
073      }
074    
075      private static boolean floating(final JToolBar toolBar) {
076        return execute(new GuiQuery<Boolean>() {
077          protected Boolean executeInEDT() {
078            return isJToolBarFloating(toolBar);
079          }
080        });
081      }
082    
083      /**
084       * Makes the given <code>{@link JToolBar}</code> float.
085       * @param toolBar the target <code>JToolBar</code>.
086       * @throws IllegalStateException if the <code>JToolBar</code> is disabled.
087       * @throws IllegalStateException if the <code>JToolBar</code> is not showing on the screen.
088       * @throws IllegalStateException if the <code>JToolBar</code> is not floatable.
089       * @throws ActionFailedException if the <code>JToolBar</code> cannot be dragged.
090       */
091      public void makeFloat(JToolBar toolBar) {
092        Pair<Point, Pair<Window, Point>> floatInfo = floatInfo(toolBar, location);
093        Point p = floatInfo.ii.ii; // ancestor location
094        doFloat(toolBar, p.x, p.y, floatInfo);
095      }
096    
097      /**
098       * Drags the <code>{@link JToolBar}</code> to the given location, causing it to float.
099       * @param toolBar the target <code>JToolBar</code>.
100       * @param x the horizontal coordinate of the location to drag the <code>JToolBar</code> to.
101       * @param y the vertical coordinate of the location to drag the <code>JToolBar</code> to.
102       * @throws IllegalStateException if the <code>JToolBar</code> is disabled.
103       * @throws IllegalStateException if the <code>JToolBar</code> is not showing on the screen.
104       * @throws IllegalStateException if the <code>JToolBar</code> is not floatable.
105       * @throws ActionFailedException if the <code>JToolBar</code> cannot be dragged.
106       */
107      @RunsInEDT
108      public void floatTo(JToolBar toolBar, int x, int y) {
109        doFloat(toolBar, x, y, floatInfo(toolBar, location));
110      }
111    
112      @RunsInEDT
113      private static Pair<Point, Pair<Window, Point>> floatInfo(final JToolBar toolBar, final JToolBarLocation location) {
114        return execute(new GuiQuery<Pair<Point, Pair<Window, Point>>>() {
115          protected Pair<Point, Pair<Window, Point>> executeInEDT() {
116            validateIsEnabledAndShowing(toolBar);
117            validateIsFloatable(toolBar);
118            Pair<Window, Point> windowAndLocation = ancestorAndLocation(toolBar);
119            return new Pair<Point, Pair<Window,Point>>(location.pointToGrab(toolBar), windowAndLocation);
120          }
121        });
122      }
123    
124      @RunsInCurrentThread
125      private static void validateIsFloatable(JToolBar toolBar) {
126        if (!toolBar.isFloatable())
127          throw new IllegalStateException(concat("JToolbar <", format(toolBar), "> is not floatable"));
128      }
129    
130      @RunsInCurrentThread
131      private static Pair<Window, Point> ancestorAndLocation(final JToolBar toolBar) {
132        Window window = getWindowAncestor(toolBar);
133        return new Pair<Window, Point>(window, window.getLocation());
134      }
135    
136      @RunsInEDT
137      private void doFloat(JToolBar toolBar, int x, int y, Pair<Point, Pair<Window, Point>> floatInfo) {
138        drag(toolBar, floatInfo.i);
139        Pair<Window, Point> locationAndAncestor = floatInfo.ii;
140        Point ancestorLocation = locationAndAncestor.ii;
141        drop(locationAndAncestor.i, new Point(x - ancestorLocation.x, y - ancestorLocation.y));
142        validateFloated(toolBar);
143      }
144    
145      @RunsInEDT
146      private static void validateFloated(final JToolBar toolBar) {
147        execute(new GuiTask() {
148          protected void executeInEDT() {
149            if (!isJToolBarFloating(toolBar))
150              throw actionFailure(concat("Unable to float JToolbar <", format(toolBar), ">"));
151          }
152        });
153      }
154    
155      /**
156       * Drop the {@link JToolBar} to the requested constraint position. The constraint position must be one of the
157       * constants <code>{@link BorderLayout#NORTH NORTH}</code>, <code>{@link BorderLayout#EAST EAST}</code>,
158       * <code>{@link BorderLayout#SOUTH SOUTH}</code>, or <code>{@link BorderLayout#WEST WEST}</code>.
159       * @param toolBar the target <code>JToolBar</code>.
160       * @param constraint the constraint position.
161       * @throws IllegalStateException if the <code>JToolBar</code> is disabled.
162       * @throws IllegalStateException if the <code>JToolBar</code> is not showing on the screen.
163       * @throws IllegalArgumentException if the constraint has an invalid value.
164       * @throws ActionFailedException if the dock container cannot be found.
165       */
166      @RunsInEDT
167      public void unfloat(JToolBar toolBar, String constraint) {
168        Pair<GenericRange<Point>, Container> unfloatInfo = unfloatInfo(toolBar, constraint, location);
169        GenericRange<Point> fromAndTo = unfloatInfo.i;
170        drag(toolBar, fromAndTo.from);
171        drop(unfloatInfo.ii, fromAndTo.to);
172        validateIsNotFloating(toolBar, constraint);
173      }
174    
175      @RunsInEDT
176      private static Pair<GenericRange<Point>, Container> unfloatInfo(final JToolBar toolBar, final String constraint,
177          final JToolBarLocation location) {
178        return execute(new GuiQuery<Pair<GenericRange<Point>, Container>>() {
179          protected Pair<GenericRange<Point>, Container> executeInEDT() {
180            validateIsEnabledAndShowing(toolBar);
181            Container dock = dockFor(toolBar);
182            Point from = location.pointToGrab(toolBar);
183            Point to = location.dockLocation(toolBar, dock, constraint);
184            return new Pair<GenericRange<Point>, Container>(new GenericRange<Point>(from, to), dock);
185          }
186        });
187      }
188    
189      @RunsInEDT
190      private static void validateIsNotFloating(final JToolBar toolBar, final String constraint) {
191        execute(new GuiTask() {
192          protected void executeInEDT() {
193            if (isJToolBarFloating(toolBar))
194              throw actionFailure(concat("Failed to dock <", format(toolBar), "> using constraint ", quote(constraint)));
195          }
196        });
197      }
198    
199      @RunsInCurrentThread
200      private static Container dockFor(final JToolBar toolBar) {
201        try {
202          return field("dockingSource").ofType(Container.class).in(toolBar.getUI()).get();
203        } catch (RuntimeException e) {
204          throw actionFailure("Unabled to determine dock for JToolBar");
205        }
206      }
207    
208      /**
209       * Closes a floating <code>{@link JToolBar}</code>, making it go back to its original container in its last known
210       * location.
211       * @param toolBar the target <code>JToolBar</code>.
212       * @throws IllegalStateException if the <code>JToolBar</code> is disabled.
213       * @throws IllegalStateException if the <code>JToolBar</code> is not showing on the screen.
214       */
215      @RunsInEDT
216      public void unfloat(JToolBar toolBar) {
217        Window w = windowAncestorOf(toolBar);
218        robot.close(w);
219      }
220    
221      @RunsInEDT
222      private static Window windowAncestorOf(final JToolBar toolBar) {
223        return execute(new GuiQuery<Window>() {
224          protected Window executeInEDT() {
225            validateIsEnabledAndShowing(toolBar);
226            return getWindowAncestor(toolBar);
227          }
228        });
229      }
230    }