/*
* Bibliothek - DockingFrames
* Library built on Java/Swing, allows the user to "drag and drop"
* panels containing any Swing-Component the developer likes to add.
*
* Copyright (C) 2007 Benjamin Sigg
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Benjamin Sigg
* benjamin_sigg@gmx.ch
* CH - Switzerland
*/
package bibliothek.gui.dock.focus;
import java.awt.Component;
import java.awt.Container;
import java.awt.FocusTraversalPolicy;
/**
* A {@link FocusTraversalPolicy} that uses the algorithms of a {@link SimplifiedFocusTraversalPolicy}
* to do its work. This policy ensures that only valid {@link Component}s
* are selected, and it respects the constraints for nested policies.
* @author Benjamin Sigg
*/
public class DockFocusTraversalPolicy extends FocusTraversalPolicy {
/** the delegate used to determine the next or previous component */
private SimplifiedFocusTraversalPolicy policy;
/** whether this policy should try to create real circles or not */
private boolean circle;
/** detects recursion in the {@link #getComponentBefore(Container, Component)} method */
private boolean recursionComponentBefore = false;
/** detects recursion in the {@link #getComponentAfter(Container, Component)} method */
private boolean recursionComponentAfter = false;
/** detects recursion in the {@link #getDefaultComponent(Container)} method */
private boolean recursionDefaultComponent = false;
/**
* Creates a new policy.
* @param policy the delegate providing algorithms for this policy
* @param circle if <code>true</code> then this policy does not respect
* the exact description of the functionality of {@link FocusTraversalPolicy}
* in order to ensure that <code>getComponentAfter( getComponentBefore( x )) == x</code> and
* <code>getComponentBefore( getComponentAfter( x )) == x</code>.
*/
public DockFocusTraversalPolicy( SimplifiedFocusTraversalPolicy policy, boolean circle ){
if( policy == null )
throw new IllegalArgumentException( "policy must not be null" );
this.policy = policy;
this.circle = circle;
}
/**
* Tells whether <code>component</code> can be focused or not.
* @param component some {@link Component} which might gain the focus.
* @return <code>true</code> if <code>component</code> is allowed to
* gain the focus, <code>false</code> otherwise
*/
protected boolean accept( Component component ){
return component.isFocusable() &&
component.isEnabled() &&
component.isDisplayable() &&
component.isShowing();
}
@Override
public Component getComponentAfter( Container container, Component component ) {
if( recursionComponentAfter ){
return policy.getAfter( container, component );
}
try{
recursionComponentAfter = true;
Component next = after( component );
while( true ){
if( next == component )
return null;
if( next == null )
return null;
if( next instanceof Container ){
Container nextContainer = (Container)next;
if( !nextContainer.isFocusCycleRoot() && nextContainer.isFocusTraversalPolicyProvider() ){
Component selected;
if( circle )
selected = nextContainer.getFocusTraversalPolicy().getFirstComponent( nextContainer );
else
selected = nextContainer.getFocusTraversalPolicy().getDefaultComponent( nextContainer );
if( selected == next )
return next;
next = selected;
continue;
}
}
if( accept( next ))
return next;
next = after( next );
}
}
finally{
recursionComponentAfter = false;
}
}
/**
* Searches the next {@link Component} which might gain the focus. This
* method searches recursively through the tree of {@link Component}s, but
* does not loop.
* @param component the currently focused {@link Component}.
* @return the next {@link Component} which might gain the focus
*/
protected Component after( Component component ){
Container provider = getRootOrProvider( component );
if( provider == null )
return null;
FocusTraversalPolicy providerPolicy = getFocusTraversalPolicy( provider );
if( providerPolicy == null )
return null;
Component result = providerPolicy.getComponentAfter( provider, component );
if( provider.isFocusCycleRoot() ){
return result;
}
else{ // is policy provider
if( providerPolicy == this ){
if( result == null || policy.getFirst( provider ) == result ){
result = after( provider );
}
}
else{
if( result == null || providerPolicy.getFirstComponent( provider ) == result ){
result = after( provider );
}
}
if( result == component )
return null;
return result;
}
}
@Override
public Component getComponentBefore( Container container, Component component ) {
if( recursionComponentBefore ){
return policy.getBefore( container, component );
}
try{
recursionComponentBefore = true;
Component previous = before( component );
while( true ){
if( previous == component )
return null;
if( previous == null )
return null;
if( previous instanceof Container ){
Container previousContainer = (Container)previous;
if( !previousContainer.isFocusCycleRoot() && previousContainer.isFocusTraversalPolicyProvider() ){
Component selected;
if( circle )
selected = previousContainer.getFocusTraversalPolicy().getLastComponent( previousContainer );
else
selected = previousContainer.getFocusTraversalPolicy().getDefaultComponent( previousContainer );
if( selected == previous )
return previous;
previous = selected;
continue;
}
}
if( accept( previous ))
return previous;
previous = after( previous );
}
}
finally{
recursionComponentBefore = false;
}
}
/**
* Searches the previous {@link Component} which might gain the focus. This
* method searches recursively through the tree of {@link Component}s, but
* does not loop.
* @param component the currently focused {@link Component}.
* @return the previous {@link Component} which might gain the focus
*/
protected Component before( Component component ){
Container provider = getRootOrProvider( component );
FocusTraversalPolicy providerPolicy = getFocusTraversalPolicy( provider );
Component result = providerPolicy.getComponentBefore( provider, component );
if( provider.isFocusCycleRoot() ){
return result;
}
else{ // is policy provider
if( providerPolicy == this ){
if( result == null || policy.getLast( provider ) == result ){
result = before( provider );
}
}
else{
if( result == null || providerPolicy.getLastComponent( provider ) == result ){
result = before( provider );
}
}
if( result == component )
return null;
return result;
}
}
@Override
public Component getDefaultComponent( Container container ) {
if( recursionDefaultComponent ){
return policy.getDefault( container );
}
try{
recursionDefaultComponent = true;
FocusTraversalPolicy providerPolicy = getFocusTraversalPolicy( container );
Component component = providerPolicy.getDefaultComponent( container );
if( component == container )
return component;
if( component instanceof Container ){
Container ccontainer = (Container)component;
if( ccontainer.isFocusCycleRoot() || ccontainer.isFocusTraversalPolicyProvider() ){
Component result = getDefaultComponent( ccontainer );
if( result != null )
return result;
}
}
return component;
}
finally{
recursionDefaultComponent = false;
}
}
@Override
public Component getFirstComponent( Container container ) {
FocusTraversalPolicy providerPolicy = getFocusTraversalPolicy( container );
Component component = providerPolicy.getDefaultComponent( container );
if( component == container )
return component;
if( component instanceof Container ){
Container ccontainer = (Container)component;
if( ccontainer.isFocusCycleRoot() || ccontainer.isFocusTraversalPolicyProvider() ){
Component result = getFirstComponent( ccontainer );
if( result != null )
return result;
}
}
return component;
}
@Override
public Component getLastComponent( Container container ) {
FocusTraversalPolicy providerPolicy = getFocusTraversalPolicy( container );
Component component = providerPolicy.getDefaultComponent( container );
if( component == container )
return component;
if( component instanceof Container ){
Container ccontainer = (Container)component;
if( ccontainer.isFocusCycleRoot() || ccontainer.isFocusTraversalPolicyProvider() ){
Component result = getLastComponent( ccontainer );
if( result != null )
return result;
}
}
return component;
}
/**
* Searches the first parent of <code>component</code> that is either
* a {@link Container#isFocusCycleRoot() focus cycle root} or
* a {@link Container#isFocusTraversalPolicyProvider() policy provider}.
* @param component some component
* @return some parent or <code>null</code>
*/
protected Container getRootOrProvider( Component component ){
Container container = component.getParent();
while( container != null ){
if( container.isFocusCycleRoot() || container.isFocusTraversalPolicyProvider() )
return container;
container = container.getParent();
}
return null;
}
/**
* Searches the {@link FocusTraversalPolicy} which should be used by
* <code>provider</code>. This method searches for a focus cycle root or
* policy provider whose traversal policy is {@link Container#isFocusTraversalPolicySet() set}.
* @param provider a focus cycle root or policy provider whose
* {@link SimplifiedFocusTraversalPolicy} is searched.
* @return the policy of <code>provider</code> or <code>null</code>
*/
protected FocusTraversalPolicy getFocusTraversalPolicy( Container provider ){
while( provider != null ){
if( provider.isFocusCycleRoot() || provider.isFocusTraversalPolicyProvider() ){
if( provider.isFocusTraversalPolicySet() ){
return provider.getFocusTraversalPolicy();
}
}
provider = provider.getParent();
}
return null;
}
}