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 java.lang.String.valueOf;
018    import static org.fest.swing.driver.JTabbedPaneTabIndexQuery.indexOfTab;
019    import static org.fest.util.Strings.concat;
020    
021    import java.awt.Point;
022    import java.awt.Rectangle;
023    
024    import javax.swing.JTabbedPane;
025    
026    import org.fest.swing.annotation.RunsInCurrentThread;
027    import org.fest.swing.exception.LocationUnavailableException;
028    import org.fest.swing.util.StringTextMatcher;
029    import org.fest.swing.util.TextMatcher;
030    import org.fest.util.VisibleForTesting;
031    
032    /**
033     * Understands a location on a <code>{@link JTabbedPane}</code> (notably a tab).
034     *
035     * @author Alex Ruiz
036     * @author Yvonne Wang
037     */
038    public class JTabbedPaneLocation {
039    
040      /**
041       * Returns the index of the first tab that matches the given <code>String</code>.
042       * <p>
043       * <b>Note:</b> This method is <b>not</b> guaranteed to be executed in the event dispatch thread (EDT.) Clients are
044       * responsible for calling this method from the EDT.
045       * </p>
046       * @param tabbedPane the target <code>JTabbedPane</code>.
047       * @param title the title to match.
048       * @return the index of the first tab that matches the given <code>String</code>.
049       * @throws LocationUnavailableException if a tab matching the given title could not be found.
050       */
051      @RunsInCurrentThread
052      public int indexOf(JTabbedPane tabbedPane, String title) {
053        return indexOf(tabbedPane, new StringTextMatcher(title));
054      }
055    
056      /**
057       * Returns the index of the first tab whose title matches the value in the given <code>{@link TextMatcher}</code>.
058       * <p>
059       * <b>Note:</b> This method is <b>not</b> guaranteed to be executed in the event dispatch thread (EDT.) Clients are
060       * responsible for calling this method from the EDT.
061       * </p>
062       * @param tabbedPane the target <code>JTabbedPane</code>.
063       * @param matcher indicates if the text of the <code>JTabbedPane</code> matches the value we are looking for.
064       * @return the index of the first tab that matches the given <code>String</code>.
065       * @throws LocationUnavailableException if a tab matching the given title could not be found.
066       */
067      @RunsInCurrentThread
068      public int indexOf(final JTabbedPane tabbedPane, final TextMatcher matcher) {
069        int index = indexOfTab(tabbedPane, matcher);
070        if (index >= 0) return index;
071        throw new LocationUnavailableException(concat(
072            "Unable to find a tab with title matching ", matcher.description(), " ", matcher.formattedValues()));
073      }
074    
075      /**
076       * Returns the coordinates of the tab under the given index.
077       * <p>
078       * <b>Note:</b> This method is <b>not</b> guaranteed to be executed in the event dispatch thread (EDT.) Clients are
079       * responsible for calling this method from the EDT.
080       * </p>
081       * @param tabbedPane the target <code>JTabbedPane</code>.
082       * @param index the given index.
083       * @return the coordinates of the tab under the given index.
084       * @throws IndexOutOfBoundsException if the given index is negative or out of bounds.
085       * @throws LocationUnavailableException if the tab under the given index is not visible.
086       */
087      @RunsInCurrentThread
088      public Point pointAt(final JTabbedPane tabbedPane, final int index) {
089        validateIndex(tabbedPane, index);
090        Rectangle rect = tabbedPane.getUI().getTabBounds(tabbedPane, index);
091        // From Abbot: TODO figure out the effects of tab layout policy sometimes tabs are not directly visible
092        if (rect == null || rect.x < 0)
093          throw new LocationUnavailableException(concat("The tab '", valueOf(index), "' is not visible"));
094        return new Point(rect.x + rect.width / 2, rect.y + rect.height / 2);
095      }
096    
097      @VisibleForTesting
098      @RunsInCurrentThread
099      void validateIndex(JTabbedPane tabbedPane, int index) {
100        int max = tabbedPane.getTabCount() - 1;
101        if (index >= 0 && index <= max) return;
102        throw new IndexOutOfBoundsException(concat(
103            "Index <", valueOf(index), "> is not within the JTabbedPane bounds of <0> and <", valueOf(max), "> (inclusive)"));
104      }
105    }