1 /* 2 * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.javafx.scene.traversal; 27 28 import com.sun.javafx.scene.NodeHelper; 29 import com.sun.javafx.scene.ParentHelper; 30 import javafx.collections.ObservableList; 31 import javafx.scene.Node; 32 import javafx.scene.Parent; 33 34 import java.util.List; 35 36 final class TabOrderHelper { 37 private static Node findPreviousFocusableInList(List<Node> nodeList, int startIndex) { 38 for (int i = startIndex ; i >= 0 ; i--) { 39 Node prevNode = nodeList.get(i); 40 // ParentTraverEngine can override traversability, so we need to check it first 41 if (isDisabledOrInvisible(prevNode)) continue; 42 final ParentTraversalEngine traversalEngine = prevNode instanceof Parent 43 ? ParentHelper.getTraversalEngine((Parent) prevNode) : null; 44 if (prevNode instanceof Parent) { 45 if (traversalEngine != null && traversalEngine.canTraverse()) { 46 Node selected = traversalEngine.selectLast(); 47 if (selected != null) { 48 return selected; 49 } 50 } else { 51 List<Node> prevNodesList = ((Parent) prevNode).getChildrenUnmodifiable(); 52 if (prevNodesList.size() > 0) { 53 Node newNode = findPreviousFocusableInList(prevNodesList, prevNodesList.size() - 1); 54 if (newNode != null) { 55 return newNode; 56 } 57 } 58 } 59 } 60 if (traversalEngine != null 61 ? traversalEngine.isParentTraversable() 62 : prevNode.isFocusTraversable()) { 63 return prevNode; 64 } 65 } 66 return null; 67 } 68 69 private static boolean isDisabledOrInvisible(Node prevNode) { 70 return prevNode.isDisabled() || !NodeHelper.isTreeVisible(prevNode); 71 } 72 73 public static Node findPreviousFocusablePeer(Node node, Parent root) { 74 Node startNode = node; 75 Node newNode = null; 76 List<Node> parentNodes = findPeers(startNode); 77 78 if (parentNodes == null) { 79 // We are at top level, so select the last focusable node 80 ObservableList<Node> rootChildren = ((Parent) node).getChildrenUnmodifiable(); 81 return findPreviousFocusableInList(rootChildren, rootChildren.size() - 1); 82 } 83 84 int ourIndex = parentNodes.indexOf(startNode); 85 86 // Start with the siblings "to the left" 87 newNode = findPreviousFocusableInList(parentNodes, ourIndex - 1); 88 89 /* 90 ** we've reached the end of the peer nodes, and none have been selected, 91 ** time to look at our parents peers..... 92 */ 93 while (newNode == null && startNode.getParent() != root) { 94 List<Node> peerNodes; 95 int parentIndex; 96 97 Parent parent = startNode.getParent(); 98 if (parent != null) { 99 // If the parent itself is traversable, select it 100 final ParentTraversalEngine parentEngine 101 = ParentHelper.getTraversalEngine(parent); 102 if (parentEngine != null ? parentEngine.isParentTraversable() : parent.isFocusTraversable()) { 103 newNode = parent; 104 } else { 105 peerNodes = findPeers(parent); 106 if (peerNodes != null) { 107 parentIndex = peerNodes.indexOf(parent); 108 newNode = findPreviousFocusableInList(peerNodes, parentIndex - 1); 109 } 110 } 111 } 112 startNode = parent; 113 } 114 115 return newNode; 116 } 117 118 private static List<Node> findPeers(Node node) { 119 List<Node> parentNodes = null; 120 Parent parent = node.getParent(); 121 /* 122 ** check that we haven't hit the top-level 123 */ 124 if (parent != null) { 125 parentNodes = parent.getChildrenUnmodifiable(); 126 } 127 return parentNodes; 128 } 129 130 private static Node findNextFocusableInList(List<Node> nodeList, int startIndex) { 131 for (int i = startIndex ; i < nodeList.size() ; i++) { 132 Node nextNode = nodeList.get(i); 133 if (isDisabledOrInvisible(nextNode)) continue; 134 final ParentTraversalEngine traversalEngine = nextNode instanceof Parent 135 ? ParentHelper.getTraversalEngine((Parent) nextNode) : null; 136 // ParentTraverEngine can override traversability, so we need to check it first 137 if (traversalEngine != null 138 ? traversalEngine.isParentTraversable() 139 : nextNode.isFocusTraversable()) { 140 return nextNode; 141 } 142 else if (nextNode instanceof Parent) { 143 if (traversalEngine!= null && traversalEngine.canTraverse()) { 144 Node selected = traversalEngine.selectFirst(); 145 if (selected != null) { 146 return selected; 147 } else { 148 // If the Parent has it's own engine, but no selection can be done, skip it 149 continue; 150 } 151 } 152 List<Node> nextNodesList = ((Parent)nextNode).getChildrenUnmodifiable(); 153 if (nextNodesList.size() > 0) { 154 Node newNode = findNextFocusableInList(nextNodesList, 0); 155 if (newNode != null) { 156 return newNode; 157 } 158 } 159 } 160 } 161 return null; 162 } 163 164 public static Node findNextFocusablePeer(Node node, Parent root, boolean traverseIntoCurrent) { 165 Node startNode = node; 166 Node newNode = null; 167 168 // First, try to find next peer among the node children 169 if (traverseIntoCurrent && node instanceof Parent) { 170 newNode = findNextFocusableInList(((Parent)node).getChildrenUnmodifiable(), 0); 171 } 172 173 // Next step is to select the siblings "to the right" 174 if (newNode == null) { 175 List<Node> parentNodes = findPeers(startNode); 176 if (parentNodes == null) { 177 // We got a top level Node that has no focusable children (we know that from the first step above), so 178 // there's nothing to do. 179 return null; 180 } 181 int ourIndex = parentNodes.indexOf(startNode); 182 newNode = findNextFocusableInList(parentNodes, ourIndex + 1); 183 } 184 185 /* 186 ** we've reached the end of the peer nodes, and none have been selected, 187 ** time to look at our parents peers..... 188 */ 189 while (newNode == null && startNode.getParent() != root) { 190 List<Node> peerNodes; 191 int parentIndex; 192 193 Parent parent = startNode.getParent(); 194 if (parent != null) { 195 peerNodes = findPeers(parent); 196 if (peerNodes != null) { 197 parentIndex = peerNodes.indexOf(parent); 198 newNode = findNextFocusableInList(peerNodes, parentIndex + 1); 199 } 200 } 201 startNode = parent; 202 } 203 204 return newNode; 205 } 206 207 public static Node getFirstTargetNode(Parent p) { 208 if (p == null || isDisabledOrInvisible(p)) return null; 209 final ParentTraversalEngine traversalEngine 210 = ParentHelper.getTraversalEngine(p); 211 if (traversalEngine!= null && traversalEngine.canTraverse()) { 212 Node selected = traversalEngine.selectFirst(); 213 if (selected != null) { 214 return selected; 215 } 216 } 217 List<Node> parentsNodes = p.getChildrenUnmodifiable(); 218 for (Node n : parentsNodes) { 219 if (isDisabledOrInvisible(n)) continue; 220 final ParentTraversalEngine parentEngine = n instanceof Parent 221 ? ParentHelper.getTraversalEngine((Parent)n) : null; 222 if (parentEngine != null ? parentEngine.isParentTraversable() : n.isFocusTraversable()) { 223 return n; 224 } 225 if (n instanceof Parent) { 226 Node result = getFirstTargetNode((Parent)n); 227 if (result != null) return result; 228 } 229 } 230 return null; 231 } 232 233 public static Node getLastTargetNode(Parent p) { 234 if (p == null || isDisabledOrInvisible(p)) return null; 235 final ParentTraversalEngine traversalEngine 236 = ParentHelper.getTraversalEngine(p); 237 if (traversalEngine!= null && traversalEngine.canTraverse()) { 238 Node selected = traversalEngine.selectLast(); 239 if (selected != null) { 240 return selected; 241 } 242 } 243 List<Node> parentsNodes = p.getChildrenUnmodifiable(); 244 for (int i = parentsNodes.size() - 1; i >= 0; --i) { 245 Node n = parentsNodes.get(i); 246 if (isDisabledOrInvisible(n)) continue; 247 248 if (n instanceof Parent) { 249 Node result = getLastTargetNode((Parent) n); 250 if (result != null) return result; 251 } 252 final ParentTraversalEngine parentEngine = n instanceof Parent 253 ? ParentHelper.getTraversalEngine((Parent) n) : null; 254 if (parentEngine != null ? parentEngine.isParentTraversable() : n.isFocusTraversable()) { 255 return n; 256 } 257 } 258 return null; 259 } 260 }