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

bibliothek.gui.dock.security.GlassedPane Maven / Gradle / Ivy

The newest version!
/*
 * 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
 * [email protected]
 * CH - Switzerland
 */

package bibliothek.gui.dock.security;

import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.util.EventListener;

import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JToolTip;
import javax.swing.SwingUtilities;

import bibliothek.gui.DockController;
import bibliothek.gui.dock.control.focus.FocusController;
import bibliothek.gui.dock.control.focus.MouseFocusObserver;
import bibliothek.gui.dock.util.PropertyKey;
import bibliothek.gui.dock.util.PropertyValue;
import bibliothek.gui.dock.util.property.ConstantPropertyFactory;
import bibliothek.util.Todo;
import bibliothek.util.Todo.Compatibility;
import bibliothek.util.Todo.Priority;
import bibliothek.util.Todo.Version;
import bibliothek.util.Workarounds;

/**
 * A panel containing two children: a "content pane" and a "glass pane". The
 * "content pane" can be replaced by the client and can be any {@link JComponent}.
 * The "glassed pane" is an invisible panel above the "content pane". It will
 * catch all {@link MouseEvent}s, inform the {@link FocusController} about
 * them, and then forward the events to the "content pane".
 * @author Benjamin Sigg
 */
@Todo( compatibility=Compatibility.COMPATIBLE, priority=Priority.MAJOR, target=Version.VERSION_1_1_2,
	description="In Java 1.7 if a mouse-dragged is followed by a mouse-exit, and the mouse is over another GlassedPane, then this GlassedPane no longer receives events that it received in Java 1.6")
public class GlassedPane extends JPanel{
	/** the strategy used by a {@link GlassedPane} to manage its tooltips */
	public static final PropertyKey TOOLTIP_STRATEGY = new PropertyKey( "tooltip strategy", 
			new ConstantPropertyFactory( new DefaultTooltipStrategy() ), true );
	
    /** An arbitrary component */
    private JComponent contentPane = new JPanel();
    /** A component lying over all other components. Catches every MouseEvent */
    private JComponent glassPane = new GlassPane();
    /** A controller which will be informed about every click of the mouse */
    private DockController controller;
    
    /** whether a {@link MouseEvent} is forwarded right now */
    private boolean onSending = false;
    
    /** the strategy used to manage tooltips */
    private PropertyValue tooltips = new PropertyValue( TOOLTIP_STRATEGY ){
		@Override
		protected void valueChanged( TooltipStrategy oldValue, TooltipStrategy newValue ){
			if( oldValue != null ){
				oldValue.uninstall( GlassedPane.this );
			}
			if( newValue != null ){
				newValue.install( GlassedPane.this );
			}
		}
	};
    
    /**
     * Creates a new pane
     */
    public GlassedPane(){
        setLayout( null );
        contentPane.setOpaque( false );
        setOpaque( false );
        
        add( glassPane );
        add( contentPane );
        setFocusCycleRoot( true );
    }
    
    /**
     * Sets the controller to inform about {@link KeyEvent}s.
     * @param controller the controller to inform
     */
    public void setController( DockController controller ){
		this.controller = controller;
		tooltips.setProperties( controller );
	}

    @Override
    public void doLayout() {
        int width = getWidth();
        int height = getHeight();
        if( contentPane != null ){
        	contentPane.setBounds( 0, 0, width, height );
        }
        glassPane.setBounds( 0, 0, width, height );
    }

    @Override
    public Dimension getPreferredSize() {
    	if( contentPane == null ){
    		return super.getPreferredSize();
    	}
        return contentPane.getPreferredSize();
    }
    @Override
    public Dimension getMaximumSize() {
    	if( contentPane == null ){
    		return super.getMaximumSize();
    	}
        return contentPane.getMaximumSize();
    }
    @Override
    public Dimension getMinimumSize() {
    	if( contentPane == null ){
    		return super.getMinimumSize();
    	}
    	return contentPane.getMinimumSize();
    }

    /**
     * Sets the center panel of this GlassedPane.
     * @param contentPane the content of this pane, a value of null will
     * just remove the current content pane
     */
    public void setContentPane( JComponent contentPane ) {
        this.contentPane = contentPane;

        removeAll();

        add( glassPane );
        if( contentPane != null ){
        	add( contentPane );
        }
    }

    /**
     * Gets the content of this pane.
     * @return the content, may be null
     */
    public JComponent getContentPane(){
        return contentPane;
    }

    /**
     * Gets the transparent panel that is lying over the ContentPane.
     * @return the GlassPane
     */
    public JComponent getGlassPane(){
        return glassPane;
    }
    
    /**
     * A panel that lies over all other components of the enclosing GlassedPane.
     * This panel catches all MouseEvent, and informs the {@link MouseFocusObserver}.
     * @author Benjamin Sigg
     */
    public class GlassPane extends JPanel implements MouseListener, MouseMotionListener, MouseWheelListener{
        /** the component where a drag-event started */
        private Component dragged;
        /** the component currently under the mouse */
        private Component over;
        /** the number of pressed buttons */
        private int downCount = 0;
        
        /** callback forwarded to the current {@link TooltipStrategy} of {@link GlassedPane#tooltips} */
        private TooltipStrategyCallback callback = new TooltipStrategyCallback(){
			public void setToolTipText( String text ){
				GlassPane.this.setToolTipText( text );
			}
			
			public String getToolTipText(){
				return GlassPane.this.getToolTipText();
			}
			
			public GlassedPane getGlassedPane(){
				return GlassedPane.this;
			}
			
			public JToolTip createToolTip(){
				return superCreateToolTip();
			}
		};
        
        /**
         * Creates a new GlassPane.
         */
        public GlassPane(){
            addMouseListener( this );
            addMouseMotionListener( this );
            addMouseWheelListener( this );

            setOpaque( false );
            
            setFocusable( false );
            
            Workarounds.getDefault().markAsGlassPane( this );
        }
        
        public void mouseClicked( MouseEvent e ) {
            if( !e.isConsumed() )
                send( e );
        }

        public void mousePressed( MouseEvent e ) {
            if( !e.isConsumed() )
                send( e );
        }

        public void mouseReleased( MouseEvent e ) {
            if( !e.isConsumed() )
                send( e );
        }

        public void mouseEntered( MouseEvent e ) {
            if( !e.isConsumed() )
                send( e );
        }

        public void mouseExited( MouseEvent e ) {
            if( !e.isConsumed() && isVisible() )
                send( e );

            if( !isVisible() ){
                downCount = 0;
            }
        }

        public void mouseDragged( MouseEvent e ) {
            if( !e.isConsumed() )
                send( e );
        }

        public void mouseMoved( MouseEvent e ) {
            if( !e.isConsumed() )
                send( e );
        }

        public void mouseWheelMoved( MouseWheelEvent e ) {
            if( !e.isConsumed() )
                send( e );
        }

        /**
         * Shorthand for send( e, e.getID() );.
         * @param e the event to send
         */
        private void send( MouseEvent e ){
            send( e, e.getID() );
        }

        /**
         * Dispatches the event e to the ContentPane or a child
         * of the ContentPane. Also informs the {@link MouseFocusObserver} about the event.
         * @param e the event to handle
         * @param id the type of the event
         */
        private void send( MouseEvent e, int id ){
        	if( !onSending ){
        		try{
        			onSending = true;
        			sendNow( e, id );
        		}
        		finally{
        			onSending = false;
        		}
        	}
        }
        
        private void sendNow( MouseEvent e, int id ){
        	if( contentPane == null ){
        		return;
        	}

            Point mouse = e.getPoint();
            Component component = SwingUtilities.getDeepestComponentAt( contentPane, mouse.x, mouse.y );
            if( component != null && !component.isEnabled() ){
            	component = null;
            }
            else{
            	component = fallThrough( component, e );
            }

            boolean drag = id == MouseEvent.MOUSE_DRAGGED;
            boolean press = id == MouseEvent.MOUSE_PRESSED;
            boolean release = id == MouseEvent.MOUSE_RELEASED;
            boolean moved = id == MouseEvent.MOUSE_MOVED;
            boolean entered = id == MouseEvent.MOUSE_ENTERED;
            boolean exited = id == MouseEvent.MOUSE_EXITED;
            
            if( drag && dragged == null )
                dragged = component;
            else if( drag )
                component = dragged;

            if( press ){
            	downCount |= 1 << e.getButton();
            }

            if( downCount > 0 && dragged != null )
                component = dragged;
            else if( downCount > 0 && dragged == null )
                dragged = component;
            else if( downCount == 0 )
                dragged = null;

            if( release ){
            	downCount &= ~(1 << e.getButton());
            }
            if( (e.getModifiersEx() & (MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON2_DOWN_MASK | MouseEvent.BUTTON3_DOWN_MASK)) == 0 ){
            	// no button is pressed currently, reset dragging
            	downCount = 0;
            	dragged = null;
            }
            boolean overNewComponent = false;
            
            if( moved || entered || exited ){
                if( over != component ){
                	overNewComponent = true;
                    if( over != null ){
                    	Point overMouse = SwingUtilities.convertPoint( this, mouse, over );
                        over.dispatchEvent( new MouseEvent( 
                                over, MouseEvent.MOUSE_EXITED, e.getWhen(), e.getModifiers(), 
                                overMouse.x, overMouse.y, e.getClickCount(), e.isPopupTrigger(), 
                                e.getButton() ));
                    }

                    over = component;

                    if( over != null ){
                    	Point overMouse = SwingUtilities.convertPoint( this, mouse, over );
                        over.dispatchEvent( new MouseEvent( 
                                over, MouseEvent.MOUSE_ENTERED, e.getWhen(), e.getModifiers(), 
                                overMouse.x, overMouse.y, e.getClickCount(), e.isPopupTrigger(), 
                                e.getButton() ));
                    }
                }
            }
            
            if( component == null ){
                setCursor( null );
                setToolTipText( null );
            }
            else{
            	mouse = SwingUtilities.convertPoint( this, mouse, component );
                MouseEvent forward = new MouseEvent( 
                        component, id, e.getWhen(), e.getModifiers(), 
                        mouse.x, mouse.y, e.getClickCount(), e.isPopupTrigger(), 
                        e.getButton() );
                
                if( controller != null ){
                	controller.getGlobalMouseDispatcher().dispatch( forward );
                }
                
                component.dispatchEvent( forward );
                
                Cursor cursor = component.getCursor();
                if( getCursor() != cursor )
                    setCursor( cursor );

                tooltips.getValue().setTooltipText( over, forward, overNewComponent, callback );
            }
        }
        
        /**
         * Assuming this {@link GlassedPane} wants to forward event to component,
         * this method can decide that component should not receive the event. Instead some
         * other {@link Component} should.
         * @param component the component which in theory should get the event
         * @param event the event to be forwarded
         * @return the component which really gets the event, can also be null or component
         */
        private Component fallThrough( Component component, MouseEvent event ){
        	Class type = null;
        	if( event.getID() == MouseEvent.MOUSE_DRAGGED || event.getID() == MouseEvent.MOUSE_MOVED ){
        		type = MouseMotionListener.class;
        	}
        	else if( event.getID() == MouseEvent.MOUSE_WHEEL ){
        		type = MouseWheelListener.class;
        	}
        	else{
        		type = MouseListener.class;
        	}
        	
        	while( component != null && component.getListeners( type ).length == 0 ){
        		component = component.getParent();
        	}
        	
        	return component;
        }

        @Override
        public JToolTip createToolTip(){
        	return tooltips.getValue().createTooltip( over, callback );
        }

        private JToolTip superCreateToolTip(){
        	return super.createToolTip();
        }
        
        /**
         * Dispatches the event e to the ContentPane or one
         * of the children of ContentPane. Also informs the focusController about
         * the event.
         * @param e the event to dispatch
         */
        private void send( MouseWheelEvent e ){
        	if( contentPane == null ){
        		return;
        	}
        	
            Point mouse = e.getPoint();
            Component component = SwingUtilities.getDeepestComponentAt( contentPane, mouse.x, mouse.y );
            if( component != null ){
                mouse = SwingUtilities.convertPoint( this, mouse, component );
                MouseWheelEvent forward = new MouseWheelEvent( 
                        component, e.getID(), e.getWhen(), e.getModifiers(), 
                        mouse.x, mouse.y, e.getClickCount(), e.isPopupTrigger(), 
                        e.getScrollType(), e.getScrollAmount(), e.getWheelRotation() );
                
                if( controller != null ){
                	controller.getGlobalMouseDispatcher().dispatch( forward );
                }
                
                component.dispatchEvent( forward );
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy