Search code examples
javaswingjframetaskbarjinternalframe

How to prevent JFrame alert effect in taskbar


I am a Java SE developer and our program only run on windows OS, the program need to open files and display in UI, we display the file contents in JInternalFrame and always setselected(true) when the file is opened.

Recently we updated our program from JRE1.6.0_41 to JRE1.8.0_144 for a major bug, and noticed that in JRE1.8.0_144 when our program(JFrame) is not focused and call setselected(true) on JInternalFrame in the JFrame, the JFrame would blink in taskbar, which JRE1.6.0_41 would not. Some of the clients think it is annoying because they have to open files frequently.

So the problem is:

(1) Can I turn the feature off?

(2) If I can't, is there an alternative way to avoid the blink effect while I can still setSelected() on JInternalFrame?

If I didn't explain it well, here is the sample code to demo the alert effect, please run the program and minimize it into taskbar switch to another window, then it should blink. I have tested it on Windows 7, Windows 8.1 and Windows 10 and they all have the same alert effect.

import javax.swing.JInternalFrame;
import javax.swing.SwingUtilities;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;

import java.awt.*;

/*
 * Copy and modified by Oracle sample code "InternalFrameDemo.java"
 */
public class Login extends JFrame {
    JDesktopPane desktop;
    private int m_iFrameCounter = 0;

    public Login() {
        super("InternalFrameDemo");

        //Make the big window be indented 50 pixels from each edge
        //of the screen.
        int inset = 50;
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        setBounds(inset, inset,
                  screenSize.width  - inset*2,
                  screenSize.height - inset*2);

        //Set up the GUI.
        desktop = new JDesktopPane(); //a specialized layered pane
        createFrame(); //create first "window"
        setContentPane(desktop);

        //Make dragging a little faster but perhaps uglier.
        desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);

        // Add new frame every second
        Thread addFrameThread = new Thread() {

            @Override
            public void run() {
                while(true) {
                    SwingUtilities.invokeLater(new Runnable() {

                        @Override
                        public void run() {
                            createFrame();
                        }
                    });
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        addFrameThread.start();
    }

    //Create a new internal frame.
    protected void createFrame() {
        JInternalFrame frame = new JInternalFrame();
        frame.setTitle("" + m_iFrameCounter);
        frame.setSize(100, 100);
        frame.setLocation(0, 0);
        frame.setVisible(true); //necessary as of 1.3
        desktop.add(frame);
        frame.moveToFront();
        try {
            frame.setSelected(true);
        } catch (java.beans.PropertyVetoException e) {}
        m_iFrameCounter++;
    }

    /**
     * Create the GUI and show it.  For thread safety,
     * this method should be invoked from the
     * event-dispatching thread.
     */
    private static void createAndShowGUI() {
        //Make sure we have nice window decorations.
        JFrame.setDefaultLookAndFeelDecorated(true);

        //Create and set up the window.
        Login frame = new Login();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //Display the window.
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        //Schedule a job for the event-dispatching thread:
        //creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }
}

Update 01

This is the update codes which take @camickr advice to add KeyboardFocusManager and WindowListener, only setselected(true) on JInternalFrame when JFrame has focus or JFrame gain focus, but the alert still happen if you switch windows frequently, I think it is because JFrame lost focus right before setselected(true) on JInternalFrame. Maybe there is a way to improve the code?

import javax.swing.JInternalFrame;
import javax.swing.SwingUtilities;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;

import java.awt.*;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.beans.PropertyVetoException;

/*
 * Copy and modified by Oracle sample code "InternalFrameDemo.java"
 */
public class Login extends JFrame {
    JDesktopPane desktop;
    private int m_iFrameCounter = 0;
    private JInternalFrame m_InternalFrameWaitSelected = null;

    public Login() {
        super("InternalFrameDemo");

        //Make the big window be indented 50 pixels from each edge
        //of the screen.
        int inset = 50;
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        setBounds(inset, inset,
                  screenSize.width  - inset*2,
                  screenSize.height - inset*2);

        //Set up the GUI.
        desktop = new JDesktopPane(); //a specialized layered pane
        createFrame(); //create first "window"
        setContentPane(desktop);

        //Make dragging a little faster but perhaps uglier.
        desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);

        /*********************************************  update codes *********************************************/ 
        //Add window listener to set last JInternalframe selected when JFrame activated
        this.addWindowListener(new WindowListener() {

            @Override
            public void windowOpened(WindowEvent e) {
            }

            @Override
            public void windowClosing(WindowEvent e) {
            }

            @Override
            public void windowClosed(WindowEvent e) {
            }

            @Override
            public void windowIconified(WindowEvent e) {
            }

            @Override
            public void windowDeiconified(WindowEvent e) {
            }

            @Override
            public void windowActivated(WindowEvent e) {
                if (m_InternalFrameWaitSelected != null) {
                    try {
                        m_InternalFrameWaitSelected.setSelected(true);
                    } catch (PropertyVetoException e1) {
                        e1.printStackTrace();
                    }
                    m_InternalFrameWaitSelected = null;
                }
            }

            @Override
            public void windowDeactivated(WindowEvent e) {
            }

        });
        /*********************************************  update codes *********************************************/

        // Add new frame every 50 millisecond
        Thread addFrameThread = new Thread() {

            @Override
            public void run() {
                while(true) {
                    SwingUtilities.invokeLater(new Runnable() {

                        @Override
                        public void run() {
                            createFrame();
                        }
                    });
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        addFrameThread.start();
    }

    //Create a new internal frame.
    protected void createFrame() {
        JInternalFrame frame = new JInternalFrame();
        frame.setTitle("" + m_iFrameCounter);
        frame.setSize(100, 100);
        frame.setLocation(0, 0);
        frame.setVisible(true); //necessary as of 1.3
        desktop.add(frame);
        frame.moveToFront();
        /*********************************************  update codes *********************************************/
        // Only setselect(true) when JFrame is focus owner
        if (KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner() != null &&
                SwingUtilities.isDescendingFrom(KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(), this)) {
            try {
                frame.setSelected(true);
            } catch (java.beans.PropertyVetoException e) {}
        } else {
            m_InternalFrameWaitSelected = frame;
        }
        /*********************************************  update codes *********************************************/
        m_iFrameCounter++;
    }

    /**
     * Create the GUI and show it.  For thread safety,
     * this method should be invoked from the
     * event-dispatching thread.
     */
    private static void createAndShowGUI() {
        //Make sure we have nice window decorations.
        JFrame.setDefaultLookAndFeelDecorated(true);

        //Create and set up the window.
        Login frame = new Login();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //Display the window.
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        //Schedule a job for the event-dispatching thread:
        //creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }
}

Update 02

Tried with getGlobalActiveWindow() and windowActivated/windowDeactivated, alert still happen if switch windows frequently. Below are the test code:

getGlobalActiveWindow()

import javax.swing.JInternalFrame;
import javax.swing.SwingUtilities;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;

import java.awt.*;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.beans.PropertyVetoException;

/*
 * Copy and modified by Oracle sample code "InternalFrameDemo.java"
 */
public class Login extends JFrame {
    JDesktopPane desktop;
    private int m_iFrameCounter = 0;
    private JInternalFrame m_InternalFrameWaitSelected = null;
    /*********************************************  update codes *********************************************/ 
    private MyDefaultKeyboardFocusManager m_MyKeyboardFocusManager = new MyDefaultKeyboardFocusManager();

    public class MyDefaultKeyboardFocusManager extends DefaultKeyboardFocusManager {

        //The method is protected, need to add public method to call it
        public Window myGetGlobalActiveWindow() {
            return this.getGlobalActiveWindow();
        }

    }
    /*********************************************  update codes *********************************************/ 

    public Login() {
        super("InternalFrameDemo");

        //Make the big window be indented 50 pixels from each edge
        //of the screen.
        int inset = 50;
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        setBounds(inset, inset,
                  screenSize.width  - inset*2,
                  screenSize.height - inset*2);

        //Set up the GUI.
        desktop = new JDesktopPane(); //a specialized layered pane
        setContentPane(desktop);

        //Make dragging a little faster but perhaps uglier.
        desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);

        /*********************************************  update codes *********************************************/ 
        KeyboardFocusManager.setCurrentKeyboardFocusManager(m_MyKeyboardFocusManager);
        /*********************************************  update codes *********************************************/

        //Add window listener to set last JInternalframe selected when JFrame activated
        this.addWindowListener(new WindowListener() {

            @Override
            public void windowOpened(WindowEvent e) {
            }

            @Override
            public void windowClosing(WindowEvent e) {
            }

            @Override
            public void windowClosed(WindowEvent e) {
            }

            @Override
            public void windowIconified(WindowEvent e) {
            }

            @Override
            public void windowDeiconified(WindowEvent e) {
            }

            @Override
            public void windowActivated(WindowEvent e) {
                if (m_InternalFrameWaitSelected != null) {
                    try {
                        m_InternalFrameWaitSelected.setSelected(true);
                    } catch (PropertyVetoException e1) {
                        e1.printStackTrace();
                    }
                    m_InternalFrameWaitSelected = null;
                }
            }

            @Override
            public void windowDeactivated(WindowEvent e) {
            }

        });

        // Add new frame every 50 millisecond
        Thread addFrameThread = new Thread() {

            @Override
            public void run() {
                while(true) {
                    SwingUtilities.invokeLater(new Runnable() {

                        @Override
                        public void run() {
                            createFrame();
                        }
                    });
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        addFrameThread.start();
    }

    //Create a new internal frame.
    protected void createFrame() {
        JInternalFrame frame = new JInternalFrame();
        frame.setTitle("" + m_iFrameCounter);
        frame.setSize(100, 100);
        frame.setLocation(0, 0);
        frame.setVisible(true); //necessary as of 1.3
        desktop.add(frame);
        frame.moveToFront();
        /*********************************************  update codes *********************************************/
        // Only setselect(true) when JFrame is active
        if (m_MyKeyboardFocusManager.myGetGlobalActiveWindow() == this) {
            try {
                frame.setSelected(true);
            } catch (java.beans.PropertyVetoException e) {}
        } else {
            m_InternalFrameWaitSelected = frame;
        }
        /*********************************************  update codes *********************************************/
        m_iFrameCounter++;
    }

    /**
     * Create the GUI and show it.  For thread safety,
     * this method should be invoked from the
     * event-dispatching thread.
     */
    private static void createAndShowGUI() {
        //Make sure we have nice window decorations.
        JFrame.setDefaultLookAndFeelDecorated(true);

        //Create and set up the window.
        Login frame = new Login();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //Display the window.
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        //Schedule a job for the event-dispatching thread:
        //creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }
}

windowActivated/windowDeactivated

import javax.swing.JInternalFrame;
import javax.swing.SwingUtilities;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;

import java.awt.*;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.beans.PropertyVetoException;

/*
 * Copy and modified by Oracle sample code "InternalFrameDemo.java"
 */
public class Login extends JFrame {
    JDesktopPane desktop;
    private int m_iFrameCounter = 0;
    private JInternalFrame m_InternalFrameWaitSelected = null;
    /*********************************************  update codes *********************************************/ 
    private boolean m_bIsFrameActive = false;
    /*********************************************  update codes *********************************************/ 

    public Login() {
        super("InternalFrameDemo");

        //Make the big window be indented 50 pixels from each edge
        //of the screen.
        int inset = 50;
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        setBounds(inset, inset,
                  screenSize.width  - inset*2,
                  screenSize.height - inset*2);

        //Set up the GUI.
        desktop = new JDesktopPane(); //a specialized layered pane
        setContentPane(desktop);

        //Make dragging a little faster but perhaps uglier.
        desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);

        /*********************************************  update codes *********************************************/ 
        //Add window listener to set last JInternalframe selected when JFrame activated
        this.addWindowListener(new WindowListener() {

            @Override
            public void windowOpened(WindowEvent e) {
            }

            @Override
            public void windowClosing(WindowEvent e) {
            }

            @Override
            public void windowClosed(WindowEvent e) {
            }

            @Override
            public void windowIconified(WindowEvent e) {
            }

            @Override
            public void windowDeiconified(WindowEvent e) {
            }

            @Override
            public void windowActivated(WindowEvent e) {
                m_bIsFrameActive = true;
                if (m_InternalFrameWaitSelected != null) {
                    try {
                        m_InternalFrameWaitSelected.setSelected(true);
                    } catch (PropertyVetoException e1) {
                        e1.printStackTrace();
                    }
                    m_InternalFrameWaitSelected = null;
                }
            }

            @Override
            public void windowDeactivated(WindowEvent e) {
                m_bIsFrameActive = false;
            }

        });
        /*********************************************  update codes *********************************************/

        // Add new frame every 50 millisecond
        Thread addFrameThread = new Thread() {

            @Override
            public void run() {
                while(true) {
                    SwingUtilities.invokeLater(new Runnable() {

                        @Override
                        public void run() {
                            createFrame();
                        }
                    });
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        addFrameThread.start();
    }

    //Create a new internal frame.
    protected void createFrame() {
        JInternalFrame frame = new JInternalFrame();
        frame.setTitle("" + m_iFrameCounter);
        frame.setSize(100, 100);
        frame.setLocation(0, 0);
        frame.setVisible(true); //necessary as of 1.3
        desktop.add(frame);
        frame.moveToFront();
        /*********************************************  update codes *********************************************/
        // Only setselect(true) when JFrame is active
        if (m_bIsFrameActive) {
            try {
                frame.setSelected(true);
            } catch (java.beans.PropertyVetoException e) {}
        } else {
            m_InternalFrameWaitSelected = frame;
        }
        /*********************************************  update codes *********************************************/
        m_iFrameCounter++;
    }

    /**
     * Create the GUI and show it.  For thread safety,
     * this method should be invoked from the
     * event-dispatching thread.
     */
    private static void createAndShowGUI() {
        //Make sure we have nice window decorations.
        JFrame.setDefaultLookAndFeelDecorated(true);

        //Create and set up the window.
        Login frame = new Login();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //Display the window.
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        //Schedule a job for the event-dispatching thread:
        //creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }
}

Solution

  • If someone is interested about the answer, after I trace down to the source code in JInternalFrame, I find out the blink effect is caused by requestFocus() when setSelected(true) is called, so I decided to inherit JInternalFrame and replace requestFocus() with requestFocusInWindow(), now the blink effect is gone.

    Here is my final solution for the issue:

    import javax.swing.JInternalFrame;
    import javax.swing.SwingUtilities;
    
    import sun.swing.SwingUtilities2;
    
    import javax.swing.InternalFrameFocusTraversalPolicy;
    import javax.swing.JDesktopPane;
    import javax.swing.JFrame;
    
    import java.awt.*;
    import java.awt.event.WindowEvent;
    import java.awt.event.WindowListener;
    import java.beans.PropertyVetoException;
    
    /*
     * Copy and modified by Oracle sample code "InternalFrameDemo.java"
     */
    public class Login extends JFrame {
        JDesktopPane desktop;
        private int m_iFrameCounter = 0;
    
        /**
         * This class is created to handle problem that program would blink in taskbar 
         * when call setSelected(true) on JInternalFrame base on JRE1.8.0_144, the only different content is
         * use lastFocusOwner.requestFocusInWindow(); instead of lastFocusOwner.requestFocus(); 
         * in method restoreSubcomponentFocus() 
         *
         */
        public class MyInternalFrame extends JInternalFrame {
    
            private Component lastFocusOwner;
            private final String BASE_JRE_VERSION = "1.8.0_144";
    
            public MyInternalFrame() {
                _checkJavaVersion();
            }
    
            public MyInternalFrame(String title) {
                super(title);
                _checkJavaVersion();
            }
    
            public MyInternalFrame(String title, boolean resizable) {
                super(title, resizable);
                _checkJavaVersion();
            }
    
            public MyInternalFrame(String title, boolean resizable, boolean closable) {
                super(title, resizable, closable);
                _checkJavaVersion();
            }
    
            public MyInternalFrame(String title, boolean resizable, boolean closable, boolean maximizable) {
                super(title, resizable, closable, maximizable);
                _checkJavaVersion();
            }
    
            public MyInternalFrame(String title, boolean resizable, boolean closable, boolean maximizable, boolean iconifiable) {
                super(title, resizable, closable, maximizable, iconifiable);
                _checkJavaVersion();
            }
    
            private void _checkJavaVersion() {
                if (!BASE_JRE_VERSION.equals(System.getProperty("java.version")))
                    System.err.println(String.format("%s is not compatible with current Java runtime version : %s ", this.getClass().toString(), System.getProperty("java.version")));
            }
    
            @Override
            public void restoreSubcomponentFocus() {
                if (isIcon()) {
                    SwingUtilities2.compositeRequestFocus(getDesktopIcon());
                }
                else {
                    Component component = KeyboardFocusManager.getCurrentKeyboardFocusManager().getPermanentFocusOwner();
                    if ((component == null) || !SwingUtilities.isDescendingFrom(component, this)) {
                        // FocusPropertyChangeListener will eventually update
                        // lastFocusOwner. As focus requests are asynchronous
                        // lastFocusOwner may be accessed before it has been correctly
                        // updated. To avoid any problems, lastFocusOwner is immediately
                        // set, assuming the request will succeed.
                        setLastFocusOwner(getMostRecentFocusOwner());
                        if (lastFocusOwner == null) {
                            // Make sure focus is restored somewhere, so that
                            // we don't leave a focused component in another frame while
                            // this frame is selected.
                            setLastFocusOwner(getContentPane());
                        }
                        lastFocusOwner.requestFocusInWindow();
                    }
                }
            }
    
            private void setLastFocusOwner(Component component) {
                lastFocusOwner = component;
            }
    
            @Override
            public Component getMostRecentFocusOwner() {
                if (isSelected()) {
                    return getFocusOwner();
                }
    
                if (lastFocusOwner != null) {
                    return lastFocusOwner;
                }
    
                FocusTraversalPolicy policy = getFocusTraversalPolicy();
                if (policy instanceof InternalFrameFocusTraversalPolicy) {
                    return ((InternalFrameFocusTraversalPolicy)policy).
                        getInitialComponent(this);
                }
    
                Component toFocus = policy.getDefaultComponent(this);
                if (toFocus != null) {
                    return toFocus;
                }
                return getContentPane();
            }
    
            @Override
            public Component getFocusOwner() {
                if (isSelected()) {
                    return lastFocusOwner;
                }
                return null;
            }
        }
    
        public Login() {
            super("InternalFrameDemo");
    
            //Make the big window be indented 50 pixels from each edge
            //of the screen.
            int inset = 50;
            Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
            setBounds(inset, inset,
                      screenSize.width  - inset*2,
                      screenSize.height - inset*2);
    
            //Set up the GUI.
            desktop = new JDesktopPane(); //a specialized layered pane
            setContentPane(desktop);
    
            //Make dragging a little faster but perhaps uglier.
            desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
    
            // Add new frame every 50 millisecond
            Thread addFrameThread = new Thread() {
    
                @Override
                public void run() {
                    while(true) {
                        SwingUtilities.invokeLater(new Runnable() {
    
                            @Override
                            public void run() {
                                createFrame();
                            }
                        });
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            };
            addFrameThread.start();
        }
    
        //Create a new internal frame.
        protected void createFrame() {
            JInternalFrame frame = new MyInternalFrame();
            frame.setTitle("" + m_iFrameCounter);
            frame.setSize(100, 100);
            frame.setLocation(0, 0);
            frame.setVisible(true); //necessary as of 1.3
            desktop.add(frame);
            frame.moveToFront();
            try {
                frame.setSelected(true);
            } catch (java.beans.PropertyVetoException e) {}
            m_iFrameCounter++;
        }
    
        /**
         * Create the GUI and show it.  For thread safety,
         * this method should be invoked from the
         * event-dispatching thread.
         */
        private static void createAndShowGUI() {
            //Make sure we have nice window decorations.
            JFrame.setDefaultLookAndFeelDecorated(true);
    
            //Create and set up the window.
            Login frame = new Login();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
            //Display the window.
            frame.setVisible(true);
        }
    
        public static void main(String[] args) {
            //Schedule a job for the event-dispatching thread:
            //creating and showing this application's GUI.
            javax.swing.SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    createAndShowGUI();
                }
            });
        }
    }