001/* [{
002Copyright 2009 Nicolas Carranza <nicarran at gmail.com>
003
004This file is part of jpen.
005
006jpen is free software: you can redistribute it and/or modify
007it under the terms of the GNU Lesser General Public License as published by
008the Free Software Foundation, either version 3 of the License,
009or (at your option) any later version.
010
011jpen is distributed in the hope that it will be useful,
012but WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014GNU Lesser General Public License for more details.
015
016You should have received a copy of the GNU Lesser General Public License
017along with jpen.  If not, see <http://www.gnu.org/licenses/>.
018}] */
019package jpen.internal;
020
021import java.awt.KeyboardFocusManager;
022import java.awt.Window;
023import java.beans.PropertyChangeEvent;
024import java.beans.PropertyChangeListener;
025import java.util.concurrent.Executors;
026import java.util.concurrent.ScheduledExecutorService;
027import java.util.concurrent.ScheduledFuture;
028import java.util.concurrent.ThreadFactory;
029import java.util.concurrent.TimeUnit;
030import javax.swing.SwingUtilities;
031/**
032Allows to keep an eye on the application active window avoiding the unnecessary null activeWindow change reported by the default KeyboardFocusManager when switching windows. 
033*/
034public final class ActiveWindowProperty
035        implements PropertyChangeListener, Runnable{
036
037        public interface Listener{
038                void activeWindowChanged(Window newWindow);
039        }
040
041        private final Listener listener;
042        private Window activeWindow;
043
044        public ActiveWindowProperty(Listener listener){
045                this.listener=listener;
046                KeyboardFocusManager keyboardFocusManager=KeyboardFocusManager.getCurrentKeyboardFocusManager();
047                keyboardFocusManager.addPropertyChangeListener("activeWindow",this);
048                activeWindow=keyboardFocusManager.getActiveWindow();
049        }
050
051        public synchronized Window get(){
052                return activeWindow;
053        }
054
055        private synchronized void set(Window activeWindow){
056                this.activeWindow=activeWindow;
057                listener.activeWindowChanged(activeWindow);
058        }
059
060        //@Override
061        public void propertyChange(PropertyChangeEvent ev){
062                Window activeWindow=(Window)ev.getNewValue();
063                if(activeWindow==this.activeWindow)
064                        return;
065                if(activeWindow==null){
066                        // if the new activeWindow is null then we do the change only after a delay to avoid unnecessary changes to null (java does change the activeWindow to null when switching).
067                        if(nullWindowTask==null || nullWindowTask.isDone())
068                                nullWindowTask=nullWindowScheduler.schedule(this, 50, TimeUnit.MILLISECONDS);
069                        return;
070                }
071                if(nullWindowTask!=null){
072                        nullWindowTask.cancel(false);
073                        nullWindowTask=null;
074                }
075                set(activeWindow);
076        }
077
078        private final ScheduledExecutorService nullWindowScheduler=Executors.newSingleThreadScheduledExecutor(new ThreadFactory(){
079                                //@Override
080                                public Thread newThread(Runnable runnable){
081                                        Thread t=new Thread(runnable, "jpen-ActiveWindow-filter");
082                                        t.setDaemon(true);
083                                        return t;
084                                }
085                        });
086        private ScheduledFuture nullWindowTask;
087
088        //@Override
089        public void run(){
090                try{
091                        SwingUtilities.invokeAndWait(nullWindowRunnable);
092                }catch(Exception ex){
093                        throw new AssertionError(ex);
094                }
095        }
096
097        private final Runnable nullWindowRunnable=new Runnable(){
098                                //@Override
099                                public void run(){
100                                        set(null);
101                                }
102                        };
103}