001/*
002 *  jDTAUS Core Utilities
003 *  Copyright (C) 2005 Christian Schulte
004 *  <cs@schulte.it>
005 *
006 *  This library is free software; you can redistribute it and/or
007 *  modify it under the terms of the GNU Lesser General Public
008 *  License as published by the Free Software Foundation; either
009 *  version 2.1 of the License, or any later version.
010 *
011 *  This library is distributed in the hope that it will be useful,
012 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
013 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 *  Lesser General Public License for more details.
015 *
016 *  You should have received a copy of the GNU Lesser General Public
017 *  License along with this library; if not, write to the Free Software
018 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
019 *
020 */
021package org.jdtaus.core.swing.util;
022
023import java.awt.Component;
024import java.awt.Cursor;
025import java.awt.KeyEventDispatcher;
026import java.awt.KeyboardFocusManager;
027import java.awt.event.KeyEvent;
028import java.awt.event.MouseAdapter;
029import java.awt.event.MouseEvent;
030import java.io.Serializable;
031import javax.swing.JComponent;
032import javax.swing.RootPaneContainer;
033import javax.swing.SwingUtilities;
034import javax.swing.UIManager;
035import org.jdtaus.core.container.ContainerFactory;
036
037/**
038 * {@code JComponent} for use as a blocking glasspane.
039 * <p>Blocking is controlled by property {@code visible}. If {@code true},
040 * the current look and feel's
041 * {@link javax.swing.LookAndFeel#provideErrorFeedback(Component)
042 * provideErrorFeedback} method is called whenever a mouse clicked
043 * {@code MouseEvent}, or a {@code KeyEvent} of type
044 * {@link KeyEvent#KEY_TYPED KEY_TYPED}, whose source is not {@code null} and
045 * descending from the parent of this component, is received. Also the cursor is
046 * set to the cursor to use when blocking is in effect. If {@code false}, the
047 * cursor is reset to the system's default cursor and no more blocking is
048 * performed. Property {@code visible} defaults to {@code false}.</p>
049 *
050 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
051 * @version $JDTAUS: BlockingGlassPane.java 8641 2012-09-27 06:45:17Z schulte $
052 */
053public final class BlockingGlassPane extends JComponent
054{
055    //--Properties--------------------------------------------------------------
056
057// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausProperties
058    // This section is managed by jdtaus-container-mojo.
059
060    /**
061     * Gets the value of property <code>defaultCursorType</code>.
062     *
063     * @return Default cursor used when blocking is in effect.
064     */
065    private java.lang.Integer getDefaultCursorType()
066    {
067        return (java.lang.Integer) ContainerFactory.getContainer().
068            getProperty( this, "defaultCursorType" );
069
070    }
071
072// </editor-fold>//GEN-END:jdtausProperties
073
074    //--------------------------------------------------------------Properties--
075    //--JComponent--------------------------------------------------------------
076
077    /**
078     * Enables or disables blocking.
079     * <p>This method registers a {@code KeyEventDispatcher} with AWT's current
080     * {@code KeyboardFocusManager} blocking the keyboard for any user
081     * interaction and updates the cursor to the cursor to use when blocking is
082     * in effect if {@code visible} is {@code true}. If {@code visible} is
083     * {@code false} the {@code KeyEventDispatcher} is removed and the cursor is
084     * reset to the system's default cursor.</p>
085     *
086     * @param visible {@code true} to enable blocking; {@code false} to disable
087     * blocking.
088     */
089    public void setVisible( final boolean visible )
090    {
091        final Runnable runnable = new Runnable()
092        {
093
094            public void run()
095            {
096                if ( visible )
097                {
098                    BlockingGlassPane.super.setVisible( true );
099                    setCursor( getBlockingCursor() );
100                    KeyboardFocusManager.getCurrentKeyboardFocusManager().
101                        addKeyEventDispatcher( keyDispatcher );
102
103                }
104                else
105                {
106                    KeyboardFocusManager.getCurrentKeyboardFocusManager().
107                        removeKeyEventDispatcher( keyDispatcher );
108
109                    setCursor( Cursor.getDefaultCursor() );
110                    BlockingGlassPane.super.setVisible( false );
111                    container.getContentPane().validate();
112                }
113            }
114        };
115
116        if ( !SwingUtilities.isEventDispatchThread() )
117        {
118            SwingUtilities.invokeLater( runnable );
119        }
120        else
121        {
122            runnable.run();
123        }
124    }
125
126    //--------------------------------------------------------------JComponent--
127    //--BlockingGlassPane-------------------------------------------------------
128
129    /** {@code KeyEventDispatcher} used for blocking. */
130    private class BlockingDispatcher implements Serializable,
131        KeyEventDispatcher
132    {
133
134        public boolean dispatchKeyEvent( final KeyEvent e )
135        {
136            final Component source = e.getComponent();
137
138            if ( source != null &&
139                SwingUtilities.isDescendingFrom( source, getParent() ) &&
140                e.getID() == KeyEvent.KEY_TYPED )
141            {
142                UIManager.getLookAndFeel().provideErrorFeedback( getParent() );
143                e.consume();
144            }
145
146            return e.isConsumed();
147        }
148    }
149    /**
150     * Container this component is registered as the glasspane with.
151     * @serial
152     */
153    private final RootPaneContainer container;
154
155    /**
156     * {@code KeyEventDispatcher} used for blocking keyboard interaction.
157     * @serial
158     */
159    private final KeyEventDispatcher keyDispatcher;
160
161    /**
162     * Cursor to use when blocking is in effect.
163     * @serial
164     */
165    private Cursor cursor;
166
167    /**
168     * Creates a new {@code BlockingGlassPane} instance taking the container
169     * this component is registered with.
170     *
171     * @param container the container this component is registered with.
172     *
173     * @throws NullPointerException if {@code container} is {@code null}.
174     */
175    public BlockingGlassPane( final RootPaneContainer container )
176    {
177        super();
178
179        if ( container == null )
180        {
181            throw new NullPointerException( "container" );
182        }
183
184        this.container = container;
185        this.keyDispatcher = new BlockingDispatcher();
186        super.setVisible( false );
187        this.setOpaque( false );
188        this.addMouseListener(
189            new MouseAdapter()
190            {
191
192                public void mouseClicked( final MouseEvent e )
193                {
194                    UIManager.getLookAndFeel().
195                        provideErrorFeedback( getParent() );
196
197                }
198            } );
199
200    }
201
202    /**
203     * Creates a new {@code BlockingGlassPane} instance taking the container
204     * this component is registered with and the cursor to use when blocking is
205     * in effect.
206     *
207     * @param container the container this component is registered with.
208     * @param cursor the cursor to use when blocking is in effect.
209     *
210     * @throws NullPointerException if either {@code container} or
211     * {@code cursor} is {@code null}.
212     */
213    public BlockingGlassPane( final RootPaneContainer container,
214                              final Cursor cursor )
215    {
216        this( container );
217
218        if ( cursor == null )
219        {
220            throw new NullPointerException( "cursor" );
221        }
222
223        this.cursor = cursor;
224    }
225
226    /**
227     * Gets the cursor used when blocking is in effect.
228     * @return the cursor used when blocking is in effect.
229     */
230    private Cursor getBlockingCursor()
231    {
232        if ( this.cursor == null )
233        {
234            this.cursor = Cursor.getPredefinedCursor(
235                this.getDefaultCursorType().intValue() );
236
237        }
238
239        return this.cursor;
240    }
241
242    //-------------------------------------------------------BlockingGlassPane--
243}