001    /**
002     * Copyright (c) 2008-2009, Piet Blok
003     * All rights reserved.
004     *
005     * Redistribution and use in source and binary forms, with or without
006     * modification, are permitted provided that the following conditions
007     * are met:
008     *
009     *   * Redistributions of source code must retain the above copyright
010     *     notice, this list of conditions and the following disclaimer.
011     *   * Redistributions in binary form must reproduce the above
012     *     copyright notice, this list of conditions and the following
013     *     disclaimer in the documentation and/or other materials provided
014     *     with the distribution.
015     *   * Neither the name of the copyright holder nor the names of the
016     *     contributors may be used to endorse or promote products derived
017     *     from this software without specific prior written permission.
018     *
019     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
020     * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
021     * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
022     * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
023     * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
024     * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
025     * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
026     * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
027     * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
028     * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
029     * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
030     */
031    
032    package org.pbjar.jxlayer.plaf.misc;
033    
034    import java.awt.Cursor;
035    import java.awt.Point;
036    import java.awt.Toolkit;
037    import java.awt.event.AWTEventListener;
038    import java.awt.event.ActionEvent;
039    import java.awt.event.ActionListener;
040    import java.awt.event.MouseEvent;
041    import java.awt.image.BufferedImage;
042    import java.util.ArrayList;
043    import java.util.List;
044    
045    import javax.swing.AbstractAction;
046    import javax.swing.Action;
047    import javax.swing.JComponent;
048    import javax.swing.JOptionPane;
049    import javax.swing.JSpinner;
050    import javax.swing.SpinnerNumberModel;
051    import javax.swing.Timer;
052    
053    import org.jdesktop.jxlayer.JXLayer;
054    
055    /**
056     * A LayerUI that hides the cursor. After a MouseEvent or MouseMoveEvent the
057     * cursor will reappear for some specified time.
058     * 
059     * @author Piet Blok
060     */
061    public final class HideCursorUI extends
062            GeneralLayerUI<JComponent, HideCursorUI.HideCursorState> {
063    
064        /**
065         * Holds state information.
066         */
067        protected static class HideCursorState {
068    
069            private final Timer timer;
070    
071            private int timeout;
072    
073            private final Cursor oldCursor;
074    
075            public HideCursorState(final JXLayer<JComponent> layer, int timeout) {
076                this.timeout = timeout;
077                oldCursor = layer.getGlassPane().getCursor();
078                this.timer = new Timer(timeout, new ActionListener() {
079    
080                    @Override
081                    public void actionPerformed(ActionEvent event) {
082                        layer.getGlassPane().setCursor(nullCursor);
083                    }
084                });
085                timer.setRepeats(false);
086            }
087    
088            public int getTimeout() {
089                return timeout;
090            }
091    
092            public void resetCursor(JXLayer<JComponent> layer) {
093                if (timeout > 0) {
094                    layer.getGlassPane().setCursor(oldCursor);
095                    timer.restart();
096                }
097            }
098    
099            public void setTimeout(int timeout) {
100                this.timeout = timeout;
101                timer.setInitialDelay(timeout);
102            }
103    
104        }
105    
106        private static final Cursor nullCursor = Toolkit.getDefaultToolkit()
107                .createCustomCursor(
108                        new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB),
109                        new Point(0, 0), "nullCursor");
110    
111        private final int timeout;
112    
113        /**
114         * Equivalent to {@code HideCursorUI(0, false)}.
115         * 
116         * @see #HideCursorUI(int, boolean)
117         */
118        public HideCursorUI() {
119            this(0, false);
120        }
121    
122        /**
123         * Equivalent to {@code HideCursorUI(timeout, false)}.
124         * 
125         * @see #HideCursorUI(int, boolean)
126         */
127        public HideCursorUI(int timeout) {
128            this(timeout, false);
129        }
130    
131        /**
132         * Create an instance. Via the timeout argument the time can be specified
133         * for how long the cursor must be shown after a MouseEvent or
134         * MouseMoveEvent. A value of 0 or negative indicates that the cursor will
135         * not be shown at all, even after a MouseEvent or MouseMoveEvent.
136         * <p>
137         * Via the enableAWTEventListener argument, {@link AWTEventListener} can be
138         * enabled or disabled.
139         * </p>
140         * 
141         * @param timeout
142         *            the timeout time in milliseconds
143         * @param enableAWTEventListener
144         *            if {@code true}, {@link AWTEventListener} will be enabled.
145         *            Specifying {@code true} Solves a problem with non editable
146         *            {@code JTextComponent}s.
147         * 
148         * @throws SecurityException
149         *             when enableAWTEventListener is {@code true} and if a security
150         *             manager exists and its {@code checkPermission} method doesn't
151         *             allow the operation.
152         * @see Toolkit#addAWTEventListener(AWTEventListener, long)
153         */
154        public HideCursorUI(int timeout, boolean enableAWTEventListener) {
155            super(enableAWTEventListener);
156            this.timeout = timeout;
157        }
158    
159        /**
160         * Get {@link Action}s that:
161         * <ol>
162         * <li>Set the cursor timeout value.<li>
163         * </ol>
164         */
165        @Override
166        public List<Action> getActions(final JXLayer<JComponent> layer) {
167            ArrayList<Action> actionList = new ArrayList<Action>();
168            actionList.addAll(super.getActions(layer));
169            /*
170             * Change the cursor timeout
171             */
172            actionList.add(new AbstractAction("Set cursor timeout") {
173    
174                private static final long serialVersionUID = 1L;
175    
176                @Override
177                public void actionPerformed(ActionEvent event) {
178                    HideCursorState state = HideCursorUI.this.getStateObject(layer);
179                    Integer timeout = state.getTimeout();
180                    JSpinner spinner = new JSpinner();
181                    SpinnerNumberModel model = (SpinnerNumberModel) spinner
182                            .getModel();
183                    model.setStepSize(100);
184                    model.setMinimum(0);
185                    spinner.setValue(timeout);
186                    if (JOptionPane.OK_OPTION == JOptionPane.showConfirmDialog(
187                            layer, spinner, "Change cursor timeout",
188                            JOptionPane.OK_CANCEL_OPTION)) {
189                        timeout = (Integer) spinner.getValue();
190                        state.setTimeout(timeout);
191                    }
192                }
193            });
194    
195            return actionList;
196        }
197    
198        private void resetCursor(JXLayer<JComponent> layer) {
199            getStateObject(layer).resetCursor(layer);
200        }
201    
202        @Override
203        protected void cleanupStateObject(HideCursorState stateObject) {
204            stateObject.timer.stop();
205        }
206    
207        @Override
208        protected HideCursorState createStateObject(JXLayer<JComponent> layer) {
209            return new HideCursorState(layer, timeout);
210        }
211    
212        @Override
213        protected void processMouseEvent(MouseEvent e, JXLayer<JComponent> layer) {
214            super.processMouseEvent(e, layer);
215            resetCursor(layer);
216        }
217    
218        @Override
219        protected void processMouseMotionEvent(MouseEvent e, JXLayer<JComponent> l) {
220            super.processMouseMotionEvent(e, l);
221            resetCursor(l);
222        }
223    
224    }