All Downloads are FREE. Search and download functionalities are using the official Maven repository.

bibliothek.gui.dock.control.DefaultFocusController Maven / Gradle / Ivy

/*
 * 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) 2010 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
 * [email protected]
 * CH - Switzerland
 */
package bibliothek.gui.dock.control;

import java.awt.Component;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import javax.swing.Timer;

import bibliothek.gui.DockController;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.DockElementRepresentative;
import bibliothek.gui.dock.control.focus.AbstractFocusController;
import bibliothek.gui.dock.control.focus.DefaultFocusRequest;
import bibliothek.gui.dock.control.focus.EnsuringFocusRequest;
import bibliothek.gui.dock.control.focus.FocusController;
import bibliothek.gui.dock.control.focus.FocusRequest;
import bibliothek.gui.dock.control.focus.FocusStrategy;
import bibliothek.gui.dock.control.focus.FocusStrategyRequest;
import bibliothek.gui.dock.event.FocusVetoListener;
import bibliothek.gui.dock.event.FocusVetoListener.FocusVeto;
import bibliothek.gui.dock.title.DockTitle;

/**
 * Default implementation of {@link FocusController}.
 * @author Benjamin Sigg
 */
public class DefaultFocusController extends AbstractFocusController {

    /** the Dockable which has currently the focus, can be null */
    private Dockable focusedDockable = null;
    
    /** true while the controller actively changes the focus */
    private boolean onFocusing = false;
    
    /** all the requests waiting for their execution */
    private List pendingRequests = new ArrayList();
    
    /** {@link Runnable}s that will be executed once {@link #pendingRequests} is empty */
    private List pendingCompletionRequests = new LinkedList();
    
    /**
     * Creates a new focus-controller
     * @param controller the owner of this controller
     */
    public DefaultFocusController( DockController controller ){
    	super( controller );
    }
    
    public boolean isOnFocusing(){
	    return onFocusing;
    }
    
    public Dockable getFocusedDockable(){
    	return focusedDockable;
    }
    
    public FocusVeto checkFocusedDockable( DockElementRepresentative source ){
    	if( source == null ){
    		return null;
    	}
    	Dockable dockable = source.getElement().asDockable();
    	if( dockable == null ){
    		return null;
    	}
    	
    	FocusVeto veto;
    	if( source instanceof DockTitle ){
    		veto = fireVetoTitle( (DockTitle)source );
    	}
    	else{
    		veto = fireVetoDockable( dockable );
    	}
    	if( veto == null ){
    		return FocusVeto.NONE;
    	}
    	return veto;
    }
    
    public FocusVeto setFocusedDockable( DockElementRepresentative source, Component component, boolean force, boolean ensureFocusSet, boolean ensureDockableFocused ){
    	DefaultFocusRequest request = new DefaultFocusRequest( source, component, force, ensureFocusSet, ensureDockableFocused );
    	focus( request );
    	return request.getVeto();
    }
    
    public void ensureFocusSet( boolean dockableOnly ){
    	Dockable dockable = focusedDockable;
    	if( dockable != null ){
    		focus( new EnsuringFocusRequest( dockable, dockableOnly ));
    	}
    }
    
    /**
     * Requests focus for the {@link Component} that is described by request. The request is either
     * executed now (if {@link FocusRequest#getDelay() delay} is 0) or in the near future. The request may be canceled either
     * because another request is executed first, because of a {@link FocusVetoListener}, or because the request contains
     * invalid data.
     * @param request the request
     */
    public void focus( FocusRequest request ){
    	Request next = new Request( request, false );
    	next.enqueue();
    }
    
    public void onFocusRequestCompletion( final Runnable run ) {
    	if( EventQueue.isDispatchThread() ){
    		synchronized( pendingRequests ) {
    			if( pendingRequests.isEmpty() || isFrozen() ){
    				run.run();
    			}
    			else{
    				pendingCompletionRequests.add( run );
    			}
    		}
    	}
    	else{
    		try {
				EventQueue.invokeAndWait( new Runnable() {
					public void run() {
						onFocusRequestCompletion( run );
					}
				} );
			} catch( InterruptedException e ) {
				// there really is not much we can do here
				e.printStackTrace();
			} catch( InvocationTargetException e ) {
				// there really is not much we can do here
				e.printStackTrace();
			}
    	}
    }
    
    private void checkCompletionRequests(){
    	EventQueue.invokeLater( new Runnable() {
			public void run() {
				synchronized ( pendingRequests ) {
					Iterator completion = pendingCompletionRequests.iterator();
					while( pendingRequests.isEmpty() && completion.hasNext() ){
						Runnable completionRequest = completion.next();
						completion.remove();
						completionRequest.run();
					}
				}
			}
		});
    }
    
    /**
     * Decides whether to execute or to refuse request.
     * @param request the request to check
     * @param dockable the dockable that would receive the focus through this request
     * @return the accepted {@link Component} or null if the
     * request is to be refused
     */
    protected Component accept( final FocusRequest request, final Dockable dockable ){
    	if( isFrozen() ){
    		return null;
    	}
    	
    	if( !request.validate( this ) ){
    		return null;
    	}
    	
    	FocusVeto veto = checkFocusedDockable( request.getSource() );
    	if( veto == null ){
    		veto = FocusVeto.NONE;
    	}
    	request.veto( veto );
    	if( veto != FocusVeto.NONE ){
    		return null;
    	}
    	
    	FocusStrategy strategy = getStrategy();
    	Component component = request.getComponent();
    	
        if( strategy != null && dockable != null ){
        	Component replacement = strategy.getFocusComponent( new FocusStrategyRequest(){
				public Component getMouseClicked(){
					return request.getComponent();
				}
				
				public Dockable getDockable(){
					return dockable;
				}
				
				public boolean excluded( Component component ){
					return !request.acceptable( component );
				}
			});
        	if( replacement != null ){
        		component = replacement;
        	}
        }
        
        if( component == null && dockable != null ){
        	component = dockable.getComponent();
        }
        
        if( component != null && pendingRequests.size() > 1 ){
        	if( !request.isHardRequest() ){
	        	if( !component.isVisible() || !component.isShowing() ){
	        		component = null;
	        	}
        	}
        }
        
        return component;
    }
    
    /**
     * Called if {@link #accept(FocusRequest, Dockable)} accepted request.
     * @param request the request to execute
     * @param dockable the element that will receive the focus
     * @param component the {@link Component} that is to be focused
     */
    protected void execute( final FocusRequest request, Dockable dockable, final Component component ){
    	// clean up
    	synchronized( pendingRequests ){
	    	for( Request pending : pendingRequests ){
	    		pending.cancel();
	    	}
	    	pendingRequests.clear();
    	}

    	boolean active = true;
    	
    	// execute
    	if( EventQueue.isDispatchThread() ){
    		active = grant( request, component );
    	}
    	else{
    		EventQueue.invokeLater( new Runnable() {
				public void run(){
					grant( request, component );
				}
			});
    	}
        
        if( active && dockable != focusedDockable ){
    		Dockable oldFocused = focusedDockable;
    		focusedDockable = dockable;
    		fireDockableFocused( oldFocused, focusedDockable );
        }
        
        checkCompletionRequests();
    }
    
    private boolean grant( FocusRequest request, Component component ){
    	FocusRequest next;
    	
    	try{
	    	onFocusing = true;
	    	next = request.grant( component );
    	}
    	finally{
    		onFocusing = false;
    	}
    	if( next != null ){
        	boolean accepted = request.getSource() == next.getSource() && component == next.getComponent();
        	Request nextRequest = new Request( next, accepted );
        	return nextRequest.enqueue();
        }
    	return true;
    }
    
    private class Request implements ActionListener{
    	/** whether this request is accepted and can be executed */
    	private boolean accepted = false;
    	
    	/** the request to execute */
    	private FocusRequest request;
    	
    	/** whether this request should silently fail */
    	private boolean canceled = false;
    	
    	/**
    	 * Creates a new request.
    	 * @param request the request to execute
    	 * @param accepted whether request has already been accepted
    	 */
    	public Request( FocusRequest request, boolean accepted ){
    		this.request = request;
    		this.accepted = accepted;
    		
    		synchronized( pendingRequests ) {
    			pendingRequests.add( this );
			}
    	}
    	
    	/**
    	 * Starts this request
    	 * @return true if the request has already been handled
    	 * false otherwise
    	 */
    	public boolean enqueue(){
    		if( request.getDelay() <= 0 ){
    			run();
    			return true;
    		}
    		else{
	    		Timer timer = new Timer( request.getDelay(), this );
	    		timer.setRepeats( false );
	    		timer.start();
	    		return false;
    		}
    	}
    	
    	/**
    	 * Stop this request from ever being executed
    	 */
    	public void cancel(){
    		canceled = true;
    	}
    	
    	/**
    	 * Gets the {@link Dockable} which receives the focus through this request.
    	 * @return the dockable or null
    	 */
    	public Dockable getDockable(){
    		DockElementRepresentative source = request.getSource();
    		if( source == null ){
    			return null;
    		}
    		return source.getElement().asDockable();
    	}
    	
    	private Component accept(){
    		if( accepted ){
    			return request.getComponent();
    		}
    		else{
    			return DefaultFocusController.this.accept( request, getDockable() );
    		}
    	}
    	
    	public void actionPerformed( ActionEvent e ){
    		run();
    	}
    	
    	private void run(){
	    	if( !canceled ){
	    		Component component = accept();
		    	if( component != null ){
		    		execute( request, getDockable(), component );
		    	}
		    	else if( request.getSource() == null && request.getComponent() == null && pendingRequests.size() == 1 ){
		    		execute( request, null, null );
		    	}
		    	else{
		    		synchronized( pendingRequests ) {
						pendingRequests.remove( this );
					}
		    	}
	    	}
    	}
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy