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 org.fest.assertions.Assertions.assertThat;
018    import static org.fest.swing.driver.ComponentStateValidator.validateIsEnabledAndShowing;
019    import static org.fest.swing.driver.JScrollBarSetValueTask.setValue;
020    import static org.fest.swing.driver.JScrollBarValueQuery.valueOf;
021    import static org.fest.swing.edt.GuiActionRunner.execute;
022    import static org.fest.util.Strings.concat;
023    
024    import java.awt.Point;
025    
026    import javax.swing.JScrollBar;
027    
028    import org.fest.swing.annotation.RunsInCurrentThread;
029    import org.fest.swing.annotation.RunsInEDT;
030    import org.fest.swing.core.Robot;
031    import org.fest.swing.edt.GuiQuery;
032    import org.fest.swing.util.GenericRange;
033    import org.fest.swing.util.Pair;
034    
035    /**
036     * Understands functional testing of <code>{@link JScrollBar}</code>s:
037     * <ul>
038     * <li>user input simulation</li>
039     * <li>state verification</li>
040     * <li>property value query</li>
041     * </ul>
042     * This class is intended for internal use only. Please use the classes in the package
043     * <code>{@link org.fest.swing.fixture}</code> in your tests.
044     *
045     * @author Yvonne Wang
046     * @author Alex Ruiz
047     */
048    public class JScrollBarDriver extends JComponentDriver {
049    
050      private static final String VALUE_PROPERTY = "value";
051    
052      private final JScrollBarLocation location = new JScrollBarLocation();
053    
054      /**
055       * Creates a new </code>{@link JScrollBarDriver}</code>.
056       * @param robot the robot to use to simulate user input.
057       */
058      public JScrollBarDriver(Robot robot) {
059        super(robot);
060      }
061    
062      /**
063       * Scrolls up (or left) one unit (usually a line).
064       * @param scrollBar the target <code>JScrollBar</code>.
065       */
066      public void scrollUnitUp(JScrollBar scrollBar) {
067        scrollUnitUp(scrollBar, 1);
068      }
069    
070      /**
071       * Scrolls up (or left) one unit (usually a line,) the given number of times.
072       * @param scrollBar the target <code>JScrollBar</code>.
073       * @param times the number of times to scroll up one unit.
074       * @throws IllegalArgumentException if <code>times</code> is less than or equal to zero.
075       * @throws IllegalStateException if the <code>JScrollBar</code> is disabled.
076       * @throws IllegalStateException if the <code>JScrollBar</code> is not showing on the screen.
077       */
078      public void scrollUnitUp(JScrollBar scrollBar, int times) {
079        validateTimes(times, "scroll up one unit");
080        Pair<Point, Integer> scrollInfo = validateAndFindScrollUnitInfo(scrollBar, location, times * -1);
081        scroll(scrollBar, scrollInfo);
082      }
083    
084      /**
085       * Scrolls down (or right) one unit (usually a line).
086       * @param scrollBar the target <code>JScrollBar</code>.
087       */
088      public void scrollUnitDown(JScrollBar scrollBar) {
089        scrollUnitDown(scrollBar, 1);
090      }
091    
092      /**
093       * Scrolls down (or right) one unit (usually a line,) the given number of times.
094       * @param scrollBar the target <code>JScrollBar</code>.
095       * @param times the number of times to scroll down one unit.
096       * @throws IllegalArgumentException if <code>times</code> is less than or equal to zero.
097       * @throws IllegalStateException if the <code>JScrollBar</code> is disabled.
098       * @throws IllegalStateException if the <code>JScrollBar</code> is not showing on the screen.
099       */
100      public void scrollUnitDown(JScrollBar scrollBar, int times) {
101        validateTimes(times, "scroll down one unit");
102        Pair<Point, Integer> scrollInfo = validateAndFindScrollUnitInfo(scrollBar, location, times);
103        scroll(scrollBar, scrollInfo);
104      }
105    
106      @RunsInEDT
107      private static Pair<Point, Integer> validateAndFindScrollUnitInfo(final JScrollBar scrollBar,
108          final JScrollBarLocation location, final int times) {
109        return execute(new GuiQuery<Pair<Point, Integer>>() {
110          protected Pair<Point, Integer> executeInEDT() {
111            validateIsEnabledAndShowing(scrollBar);
112            return scrollUnitInfo(scrollBar, location, times);
113          }
114        });
115      }
116    
117      @RunsInCurrentThread
118      private static Pair<Point, Integer> scrollUnitInfo(JScrollBar scrollBar, JScrollBarLocation location, int times) {
119        Point where = blockLocation(scrollBar, location, times);
120        int count = times * scrollBar.getUnitIncrement();
121        return new Pair<Point, Integer>(where, scrollBar.getValue() + count);
122      }
123    
124      /**
125       * Scrolls up (or left) one block (usually a page).
126       * @param scrollBar the target <code>JScrollBar</code>.
127       */
128      @RunsInEDT
129      public void scrollBlockUp(JScrollBar scrollBar) {
130        scrollBlockUp(scrollBar, 1);
131      }
132    
133      /**
134       * Scrolls up (or left) one block (usually a page,) the given number of times.
135       * @param scrollBar the target <code>JScrollBar</code>.
136       * @param times the number of times to scroll up one block.
137       * @throws IllegalArgumentException if <code>times</code> is less than or equal to zero.
138       * @throws IllegalStateException if the <code>JScrollBar</code> is disabled.
139       * @throws IllegalStateException if the <code>JScrollBar</code> is not showing on the screen.
140       */
141      @RunsInEDT
142      public void scrollBlockUp(JScrollBar scrollBar, int times) {
143        validateTimes(times, "scroll up one block");
144        Pair<Point, Integer> scrollInfo = validateAndFindScrollBlockInfo(scrollBar, location, times * -1);
145        scroll(scrollBar, scrollInfo);
146      }
147    
148      /**
149       * Scrolls down (or right) one block (usually a page).
150       * @param scrollBar the target <code>JScrollBar</code>.
151       */
152      @RunsInEDT
153      public void scrollBlockDown(JScrollBar scrollBar) {
154        scrollBlockDown(scrollBar, 1);
155      }
156    
157      /**
158       * Scrolls down (or right) one block (usually a page,) the given number of times.
159       * @param scrollBar the target <code>JScrollBar</code>.
160       * @param times the number of times to scroll down one block.
161       * @throws IllegalArgumentException if <code>times</code> is less than or equal to zero.
162       * @throws IllegalStateException if the <code>JScrollBar</code> is disabled.
163       * @throws IllegalStateException if the <code>JScrollBar</code> is not showing on the screen.
164       */
165      @RunsInEDT
166      public void scrollBlockDown(JScrollBar scrollBar, int times) {
167        validateTimes(times, "scroll down one block");
168        Pair<Point, Integer> scrollInfo = validateAndFindScrollBlockInfo(scrollBar, location, times);
169        scroll(scrollBar, scrollInfo);
170      }
171    
172      private void validateTimes(int times, String action) {
173        if (times > 0) return;
174        String message = concat(
175            "The number of times to ", action, " should be greater than zero, but was <", times, ">");
176        throw new IllegalArgumentException(message);
177      }
178    
179      @RunsInEDT
180      private static Pair<Point, Integer> validateAndFindScrollBlockInfo(final JScrollBar scrollBar,
181          final JScrollBarLocation location, final int times) {
182        return execute(new GuiQuery<Pair<Point, Integer>>() {
183          protected Pair<Point, Integer> executeInEDT() {
184            validateIsEnabledAndShowing(scrollBar);
185            return scrollBlockInfo(scrollBar, location, times);
186          }
187        });
188      }
189    
190      @RunsInCurrentThread
191      private static Pair<Point, Integer> scrollBlockInfo(JScrollBar scrollBar, JScrollBarLocation location, int times) {
192        Point where = blockLocation(scrollBar, location, times);
193        int count = times * scrollBar.getBlockIncrement();
194        return new Pair<Point, Integer>(where, scrollBar.getValue() + count);
195      }
196    
197      @RunsInCurrentThread
198      private static Point blockLocation(JScrollBar scrollBar, JScrollBarLocation location, int times) {
199        if (times > 0) return location.blockLocationToScrollDown(scrollBar);
200        return location.blockLocationToScrollUp(scrollBar);
201      }
202    
203      @RunsInEDT
204      private void scroll(JScrollBar scrollBar, Pair<Point, Integer> scrollInfo) {
205        // For now, do it programmatically, faking the mouse movement and clicking
206        robot.moveMouse(scrollBar, scrollInfo.i);
207        setValueProperty(scrollBar, scrollInfo.ii);
208      }
209    
210      /**
211       * Scrolls to the maximum position of the given <code>{@link JScrollBar}</code>.
212       * @param scrollBar the target <code>JScrollBar</code>.
213       * @throws IllegalStateException if the <code>JScrollBar</code> is disabled.
214       * @throws IllegalStateException if the <code>JScrollBar</code> is not showing on the screen.
215       */
216      @RunsInEDT
217      public void scrollToMaximum(JScrollBar scrollBar) {
218        Pair<Integer, GenericRange<Point>> scrollInfo = validateAndFindScrollToMaximumInfo(scrollBar, location);
219        scroll(scrollBar, scrollInfo.i, scrollInfo.ii);
220      }
221    
222      @RunsInEDT
223      private static Pair<Integer, GenericRange<Point>> validateAndFindScrollToMaximumInfo(final JScrollBar scrollBar,
224          final JScrollBarLocation location) {
225        return execute(new GuiQuery<Pair<Integer, GenericRange<Point>>>() {
226          protected Pair<Integer, GenericRange<Point>> executeInEDT() {
227            validateIsEnabledAndShowing(scrollBar);
228            int position = scrollBar.getMaximum();
229            GenericRange<Point> scrollInfo = scrollInfo(scrollBar, location, position);
230            return new Pair<Integer, GenericRange<Point>>(position, scrollInfo);
231          }
232        });
233      }
234    
235      /**
236       * Scrolls to the minimum position of the given <code>{@link JScrollBar}</code>.
237       * @param scrollBar the target <code>JScrollBar</code>.
238       * @throws IllegalStateException if the <code>JScrollBar</code> is disabled.
239       * @throws IllegalStateException if the <code>JScrollBar</code> is not showing on the screen.
240       */
241      @RunsInEDT
242      public void scrollToMinimum(JScrollBar scrollBar) {
243        Pair<Integer, GenericRange<Point>> scrollInfo = validateAndFindScrollToMinimumInfo(scrollBar, location);
244        scroll(scrollBar, scrollInfo.i, scrollInfo.ii);
245      }
246    
247      @RunsInEDT
248      private static Pair<Integer, GenericRange<Point>> validateAndFindScrollToMinimumInfo(final JScrollBar scrollBar,
249          final JScrollBarLocation location) {
250        return execute(new GuiQuery<Pair<Integer, GenericRange<Point>>>() {
251          protected Pair<Integer, GenericRange<Point>> executeInEDT() {
252            validateIsEnabledAndShowing(scrollBar);
253            int position = scrollBar.getMinimum();
254            GenericRange<Point> scrollInfo = scrollInfo(scrollBar, location, position);
255            return new Pair<Integer, GenericRange<Point>>(position, scrollInfo);
256          }
257        });
258      }
259    
260      /**
261       * Scrolls to the given position.
262       * @param scrollBar the target <code>JScrollBar</code>.
263       * @param position the position to scroll to.
264       * @throws IllegalStateException if the <code>JScrollBar</code> is disabled.
265       * @throws IllegalStateException if the <code>JScrollBar</code> is not showing on the screen.
266       * @throws IllegalArgumentException if the given position is not within the <code>JScrollBar</code> bounds.
267       */
268      @RunsInEDT
269      public void scrollTo(JScrollBar scrollBar, int position) {
270        GenericRange<Point> scrollInfo = validateAndFindScrollInfo(scrollBar, location, position);
271        scroll(scrollBar, position, scrollInfo);
272      }
273    
274      @RunsInEDT
275      private static GenericRange<Point> validateAndFindScrollInfo(final JScrollBar scrollBar,
276          final JScrollBarLocation location, final int position) {
277        return execute(new GuiQuery<GenericRange<Point>>() {
278          protected GenericRange<Point> executeInEDT() {
279            validatePosition(scrollBar, position);
280            validateIsEnabledAndShowing(scrollBar);
281            return scrollInfo(scrollBar, location, position);
282          }
283        });
284      }
285    
286      @RunsInCurrentThread
287      private static void validatePosition(JScrollBar scrollBar, int position) {
288        int min = scrollBar.getMinimum();
289        int max = scrollBar.getMaximum();
290        if (position >= min && position <= max) return;
291        throw new IllegalArgumentException(concat(
292            "Position <", position, "> is not within the JScrollBar bounds of <", min, "> and <", max, ">"));
293      }
294    
295      @RunsInCurrentThread
296      private static GenericRange<Point> scrollInfo(JScrollBar scrollBar, JScrollBarLocation location, int position) {
297        Point from = location.thumbLocation(scrollBar, scrollBar.getValue());
298        Point to = location.thumbLocation(scrollBar, position);
299        return new GenericRange<Point>(from, to);
300      }
301    
302      private void scroll(JScrollBar scrollBar, int position, GenericRange<Point> points) {
303        simulateScrolling(scrollBar, points);
304        setValueProperty(scrollBar, position);
305      }
306    
307      @RunsInEDT
308      private void simulateScrolling(JScrollBar scrollBar, GenericRange<Point> points) {
309        robot.moveMouse(scrollBar, points.from);
310        robot.moveMouse(scrollBar, points.to);
311      }
312    
313      @RunsInEDT
314      private void setValueProperty(JScrollBar scrollBar, int value) {
315        setValue(scrollBar, value);
316        robot.waitForIdle();
317      }
318    
319      /**
320       * Asserts that the value of the <code>{@link JScrollBar}</code> is equal to the given one.
321       * @param scrollBar the target <code>JScrollBar</code>.
322       * @param value the expected value.
323       * @throws AssertionError if the value of the <code>JScrollBar</code> is not equal to the given one.
324       */
325      @RunsInEDT
326      public void requireValue(JScrollBar scrollBar, int value) {
327        assertThat(valueOf(scrollBar)).as(propertyName(scrollBar, VALUE_PROPERTY)).isEqualTo(value);
328      }
329    }