001 /* 002 * Copyright 2002-2004 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 005 * use this file except in compliance with the License. You may obtain a copy of 006 * the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 013 * License for the specific language governing permissions and limitations under 014 * the License. 015 */ 016 package org.springframework.richclient.util; 017 018 import java.awt.Component; 019 import java.awt.KeyboardFocusManager; 020 import java.awt.Window; 021 import java.util.ArrayList; 022 import java.util.Comparator; 023 import java.util.HashMap; 024 import java.util.Iterator; 025 import java.util.List; 026 import java.util.Map; 027 028 import javax.swing.JComponent; 029 import javax.swing.LayoutFocusTraversalPolicy; 030 031 /** 032 * A LayoutFocusTraversalPolicy that allows for individual containers to have a 033 * custom focus order. 034 * 035 * @author oliverh 036 */ 037 public class CustomizableFocusTraversalPolicy extends LayoutFocusTraversalPolicy { 038 039 private static final String FOCUS_ORDER_PROPERTY_NAME = "customFocusOrder"; 040 041 /** 042 * Installs an instance of CustomizableFocusTraversalPolicy as the default 043 * focus traversal policy. 044 */ 045 public static void installCustomizableFocusTraversalPolicy() { 046 KeyboardFocusManager.getCurrentKeyboardFocusManager().setDefaultFocusTraversalPolicy( 047 new CustomizableFocusTraversalPolicy()); 048 } 049 050 /** 051 * Sets a custom focus traversal order for the given container. Child 052 * components for which there is no order specified will receive focus after 053 * components that do have an order specified in the standard "layout" 054 * order. 055 * 056 * @param container 057 * the container 058 * @param componentsInOrder 059 * a list of child components in the order that thay should 060 * receive focus 061 */ 062 public static void customizeFocusTraversalOrder(JComponent container, List componentsInOrder) { 063 for (Iterator i = componentsInOrder.iterator(); i.hasNext();) { 064 Component comp = (Component)i.next(); 065 if (comp.getParent() != container) { 066 throw new IllegalArgumentException("Component [" + comp + "] is not a child of [" + container + "]."); 067 } 068 } 069 container.putClientProperty(FOCUS_ORDER_PROPERTY_NAME, createOrderMapFromList(componentsInOrder)); 070 } 071 072 private static Map createOrderMapFromList(List componentsInOrder) { 073 HashMap orderMap = new HashMap(componentsInOrder.size()); 074 for (int i = 0; i < componentsInOrder.size(); i++) { 075 orderMap.put(componentsInOrder.get(i), new Integer(i)); 076 } 077 return orderMap; 078 } 079 080 /** 081 * Creates a new CustomizableFocusTraversalPolicy 082 */ 083 public CustomizableFocusTraversalPolicy() { 084 setComparator(new CustomizableFocusTraversalComparator(getComparator())); 085 } 086 087 private static class CustomizableFocusTraversalComparator implements Comparator { 088 089 private Comparator layoutComparator; 090 091 private CustomizableFocusTraversalComparator(Comparator layoutComparator) { 092 this.layoutComparator = layoutComparator; 093 } 094 095 public int compare(Object o1, Object o2) { 096 Component comp1 = (Component)o1; 097 Component comp2 = (Component)o2; 098 if (comp1 == comp2) { 099 return 0; 100 } 101 Map order = getFocusOrder(comp1); 102 if (order != null && comp1.getParent() == comp2.getParent()) { 103 return compareSameParent(order, comp1, comp2); 104 } 105 else if (comp1.getParent() != comp2.getParent()) { 106 return compareClosestAncestor(comp1, comp2); 107 } 108 else { 109 return layoutComparator.compare(comp1, comp2); 110 } 111 } 112 113 private Map getFocusOrder(Component comp) { 114 Component parent = comp.getParent(); 115 return (Map)((parent instanceof JComponent) ? ((JComponent)parent) 116 .getClientProperty(FOCUS_ORDER_PROPERTY_NAME) : null); 117 } 118 119 private int compareSameParent(Map order, Component comp1, Component comp2) { 120 Integer index1 = (Integer)order.get(comp1); 121 Integer index2 = (Integer)order.get(comp2); 122 if (index1 != null && index2 != null) { 123 return index1.intValue() - index2.intValue(); 124 } 125 else if (index1 != null) { 126 return -1; 127 } 128 else if (index2 != null) { 129 return 1; 130 } 131 else { 132 return layoutComparator.compare(comp1, comp2); 133 } 134 } 135 136 public int compareClosestAncestor(Component comp1, Component comp2) { 137 List comp1Ancestors = getAncestors(comp1); 138 List comp2Ancestors = getAncestors(comp2); 139 int index1 = comp1Ancestors.size(); 140 int index2 = comp2Ancestors.size(); 141 while (true) { 142 if (index1 > 0) { 143 comp1 = (Component)comp1Ancestors.get(--index1); 144 } 145 else { 146 return -1; 147 } 148 if (index2 > 0) { 149 comp2 = (Component)comp2Ancestors.get(--index2); 150 } 151 else { 152 return 1; 153 } 154 if (comp1 != comp2) { 155 break; 156 } 157 } 158 return compare(comp1, comp2); 159 } 160 161 private List getAncestors(Component comp) { 162 List ancestors = new ArrayList(); 163 while (comp != null) { 164 if (comp instanceof Window) { 165 break; 166 } 167 ancestors.add(comp); 168 comp = comp.getParent(); 169 } 170 return ancestors; 171 } 172 } 173 }