001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2016 005 * Space Science and Engineering Center (SSEC) 006 * University of Wisconsin - Madison 007 * 1225 W. Dayton Street, Madison, WI 53706, USA 008 * https://www.ssec.wisc.edu/mcidas 009 * 010 * All Rights Reserved 011 * 012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 013 * some McIDAS-V source code is based on IDV and VisAD source code. 014 * 015 * McIDAS-V is free software; you can redistribute it and/or modify 016 * it under the terms of the GNU Lesser Public License as published by 017 * the Free Software Foundation; either version 3 of the License, or 018 * (at your option) any later version. 019 * 020 * McIDAS-V is distributed in the hope that it will be useful, 021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 023 * GNU Lesser Public License for more details. 024 * 025 * You should have received a copy of the GNU Lesser Public License 026 * along with this program. If not, see http://www.gnu.org/licenses. 027 */ 028 029package edu.wisc.ssec.mcidasv.util; 030 031import java.awt.Component; 032import java.awt.Container; 033import java.awt.FocusTraversalPolicy; 034import java.util.Collection; 035 036/** 037 * Abstraction of {@link FocusTraversalPolicy} that allows for easy creation of 038 * the traversal policy. 039 * 040 * <p>Note that the {@literal "delta"} parameter of both 041 * {@link #cycle(Component, int)} and {@link #indexCycle(int, int)} can be any 042 * positive or negative integer. Both methods compute indices using the wonders 043 * of modular arithmetic. 044 */ 045public class FocusTraveller extends FocusTraversalPolicy { 046 047 /** Components to traverse, stored in the desired traversal order. */ 048 private final Component[] components; 049 050 /** 051 * Creates the {@link FocusTraversalPolicy}. 052 * 053 * @param componentsToTraverse Components to traverse, in the desired order. 054 * Cannot be {@code null}. 055 */ 056 public FocusTraveller(final Component... componentsToTraverse) { 057 components = componentsToTraverse; 058 } 059 060 /** 061 * Creates the {@link FocusTraversalPolicy} for the given components. 062 * 063 * @param componentsToTraverse Components to traverse. Cannot be {@code null}. 064 */ 065 public FocusTraveller(final Collection<Component> componentsToTraverse) { 066 components = componentsToTraverse.toArray(new Component[0]); 067 } 068 069 /** 070 * Cycles through valid index values. 071 * 072 * @param index Current index. 073 * @param delta Index of next component, relative to {@code index}. 074 * 075 * @return Next index value. 076 */ 077 private int indexCycle(final int index, final int delta) { 078 int size = components.length; 079 return (index + delta + size) % size; 080 } 081 082 /** 083 * Cycles through components. 084 * 085 * @param currentComponent Cannot be {@code null}. 086 * @param delta Index of next component, relative to {@code currentComponent}. 087 * 088 * @return The {@literal "next"} component in the traversal order. 089 */ 090 private Component cycle(final Component currentComponent, final int delta) { 091 int index = -1; 092 loop: for (int i = 0; i < components.length; i++) { 093 Component component = components[i]; 094 for (Component c = currentComponent; c != null; c = c.getParent()) { 095 if (component == c) { 096 index = i; 097 break loop; 098 } 099 } 100 } 101 102 // try to find enabled component in "delta" direction 103 int initialIndex = index; 104 while (true) { 105 int newIndex = indexCycle(index, delta); 106 if (newIndex == initialIndex) { 107 break; 108 } 109 index = newIndex; 110 111 Component component = components[newIndex]; 112 if (component.isEnabled() && component.isVisible() && component.isFocusable()) { 113 return component; 114 } 115 } 116 // not found 117 return currentComponent; 118 } 119 120 /** 121 * Gets the next component after {@code component}. 122 * 123 * @param container Ignored. 124 * @param component Cannot be {@code null}. 125 * 126 * @return Next component after {@code component}. 127 */ 128 public Component getComponentAfter(final Container container, final Component component) { 129 return cycle(component, 1); 130 } 131 132 /** 133 * Gets the component before {@code component}. 134 * 135 * @param container Ignored. 136 * @param component Cannot be {@code null}. 137 * 138 * @return The {@link Component} before {@code component} in traversal order. 139 */ 140 public Component getComponentBefore(final Container container, final Component component) { 141 return cycle(component, -1); 142 } 143 144 /** 145 * Gets the first component. 146 * 147 * @param container Ignored. 148 * 149 * @return The first {@link Component} in traversal order. 150 */ 151 public Component getFirstComponent(final Container container) { 152 return components[0]; 153 } 154 155 /** 156 * Gets the last component. 157 * 158 * @param container Ignored. 159 * 160 * @return The last {@link Component} in traversal order. 161 */ 162 public Component getLastComponent(final Container container) { 163 return components[components.length - 1]; 164 } 165 166 /** 167 * Not used. See {@link #getFirstComponent(Container)}. 168 * 169 * @param container Ignored. 170 * 171 * @return The first {@link Component} in traversal order. 172 */ 173 public Component getDefaultComponent(final Container container) { 174 return getFirstComponent(container); 175 } 176}