/*
 * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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 General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.apple.laf;

import java.awt.*;
import java.awt.event.*;
import java.lang.reflect.*;
import java.security.*;
import java.util.*;

import javax.swing.*;

public class ScreenMenuBar extends MenuBar implements ContainerListener, ScreenMenuPropertyHandler, ComponentListener {
    static boolean sJMenuBarHasHelpMenus = false; //$ could check by calling getHelpMenu in a try block

    JMenuBar fSwingBar;
    Hashtable<JMenu, ScreenMenu> fSubmenus;

    ScreenMenuPropertyListener fPropertyListener;
    ScreenMenuPropertyListener fAccessibleListener;

    public ScreenMenuBar(final JMenuBar swingBar) {
        fSwingBar = swingBar;
        fSubmenus = new Hashtable<JMenu, ScreenMenu>(fSwingBar.getMenuCount());
    }

    public void addNotify() {
        super.addNotify();

        fSwingBar.addContainerListener(this);
        fPropertyListener = new ScreenMenuPropertyListener(this);
        fSwingBar.addPropertyChangeListener(fPropertyListener);
        fAccessibleListener = new ScreenMenuPropertyListener(this);
        fSwingBar.getAccessibleContext().addPropertyChangeListener(fAccessibleListener);

        // We disable component events when the menu bar is not parented.  So now we need to
        // sync back up with the current state of the JMenuBar.  We first add the menus we
        // don't have and then remove the items that are no longer on the JMenuBar.
        final int count = fSwingBar.getMenuCount();
        for(int i = 0; i < count ; i++) {
            final JMenu m = fSwingBar.getMenu(i);
            if (m != null) {
                addSubmenu(m);
            }
        }

        final Enumeration<JMenu> e = fSubmenus.keys();
        while (e.hasMoreElements()) {
            final JMenu m = e.nextElement();
            if (fSwingBar.getComponentIndex(m) == -1) {
                removeSubmenu(m);
            }
        }
    }

    public void removeNotify() {
        // KCH - 3974930 - We do null checks for fSwingBar and fSubmenus because some people are using
        // reflection to muck about with our ivars
        if (fSwingBar != null) {
            fSwingBar.removePropertyChangeListener(fPropertyListener);
            fSwingBar.getAccessibleContext().removePropertyChangeListener(fAccessibleListener);
            fSwingBar.removeContainerListener(this);
        }

        fPropertyListener = null;
        fAccessibleListener = null;

        if (fSubmenus != null) {
            // We don't listen to events when the menu bar is not parented.
            // Remove all the component listeners.
            final Enumeration<JMenu> e = fSubmenus.keys();
            while (e.hasMoreElements()) {
                final JMenu m = e.nextElement();
                m.removeComponentListener(this);
            }
        }

        super.removeNotify();
    }

    
Invoked when a component has been added to the container.
/** * Invoked when a component has been added to the container. */
public void componentAdded(final ContainerEvent e) { final Component child = e.getChild(); if (!(child instanceof JMenu)) return; addSubmenu((JMenu)child); }
Invoked when a component has been removed from the container.
/** * Invoked when a component has been removed from the container. */
public void componentRemoved(final ContainerEvent e) { final Component child = e.getChild(); if (!(child instanceof JMenu)) return; removeSubmenu((JMenu)child); }
Invoked when the component's size changes.
/** * Invoked when the component's size changes. */
public void componentResized(final ComponentEvent e) {}
Invoked when the component's position changes.
/** * Invoked when the component's position changes. */
public void componentMoved(final ComponentEvent e) {}
Invoked when the component has been made visible. See componentHidden - we should still have a MenuItem it just isn't inserted
/** * Invoked when the component has been made visible. * See componentHidden - we should still have a MenuItem * it just isn't inserted */
public void componentShown(final ComponentEvent e) { final Object source = e.getSource(); if (!(source instanceof JMenuItem)) return; setChildVisible((JMenuItem)source, true); }
Invoked when the component has been made invisible. MenuComponent.setVisible does nothing, so we remove the ScreenMenuItem from the ScreenMenu but leave it in fItems
/** * Invoked when the component has been made invisible. * MenuComponent.setVisible does nothing, * so we remove the ScreenMenuItem from the ScreenMenu * but leave it in fItems */
public void componentHidden(final ComponentEvent e) { final Object source = e.getSource(); if (!(source instanceof JMenuItem)) return; setChildVisible((JMenuItem)source, false); } /* * MenuComponent.setVisible does nothing, * so we just add or remove the child from the ScreenMenuBar * but leave it in the list */ public void setChildVisible(final JMenuItem child, final boolean b) { if (child instanceof JMenu) { if (b) { addSubmenu((JMenu)child); } else { final ScreenMenu sm = fSubmenus.get(child); if (sm != null) remove(sm); } } } public void removeAll() { synchronized (getTreeLock()) { final int nitems = getMenuCount(); for (int i = nitems-1 ; i >= 0 ; i--) { remove(i); } } } public void setIcon(final Icon i) {} public void setLabel(final String s) {} public void setEnabled(final boolean b) { final int count = fSwingBar.getMenuCount(); for (int i = 0; i < count; i++) { fSwingBar.getMenu(i).setEnabled(b); } } public void setAccelerator(final KeyStroke ks) {} public void setToolTipText(final String tooltip) {} // only check and radio items can be indeterminate public void setIndeterminate(boolean indeterminate) { } ScreenMenu addSubmenu(final JMenu m) { ScreenMenu sm = fSubmenus.get(m); if (sm == null) { sm = new ScreenMenu(m); m.addComponentListener(this); fSubmenus.put(m, sm); } sm.setEnabled(m.isEnabled()); // MenuComponents don't support setVisible, so we just don't add it to the menubar if (m.isVisible() && sm.getParent() == null) { int newIndex = 0, currVisibleIndex = 0; JMenu menu = null; final int menuCount = fSwingBar.getMenuCount(); for (int i = 0; i < menuCount; i++) { menu = fSwingBar.getMenu(i); if (menu == m) { newIndex = currVisibleIndex; break; } if (menu != null && menu.isVisible()) { currVisibleIndex++; } } add(sm, newIndex); } return sm; }
Remove the screen menu associated with the specifiec menu. This also removes any associated component listener on the screen menu and removes the key/value (menu/screen menu) from the fSubmenus cache.
Params:
  • menu – The swing menu we want to remove the screen menu for.
/** * Remove the screen menu associated with the specifiec menu. This * also removes any associated component listener on the screen menu * and removes the key/value (menu/screen menu) from the fSubmenus cache. * * @param menu The swing menu we want to remove the screen menu for. */
private void removeSubmenu(final JMenu menu) { final ScreenMenu screenMenu = fSubmenus.get(menu); if (screenMenu == null) return; menu.removeComponentListener(this); remove(screenMenu); fSubmenus.remove(menu); } private static Field[] stolenFields = null; static { stolenFields = AccessController.doPrivileged(new PrivilegedAction<Field[]>() { public Field[] run() { try { final Field[] localFields = new Field[2]; localFields[0] = MenuBar.class.getDeclaredField("menus"); localFields[1] = MenuComponent.class.getDeclaredField("parent"); AccessibleObject.setAccessible(localFields, true); return localFields; } catch (final NoSuchFieldException nsf) { // If this happens, Sun changed the definition of MenuBar and MenuComponent! nsf.printStackTrace(System.err); return null; } } }); }; public Menu add(final Menu m, final int index) { synchronized (getTreeLock()) { if (m.getParent() != null) { m.getParent().remove(m); } // Use nasty reflection to get at the menus array and parent fields. try { if (stolenFields == null) return m; final Vector<Menu> menus = (Vector<Menu>)stolenFields[0].get(this); menus.insertElementAt(m, index); stolenFields[1].set(m, this); final sun.lwawt.macosx.CMenuBar peer = (sun.lwawt.macosx.CMenuBar)getPeer(); if (peer == null) return m; peer.setNextInsertionIndex(index); if (m.getPeer() == null) { m.addNotify(); } peer.setNextInsertionIndex(-1); } catch (final IllegalAccessException iae) { iae.printStackTrace(System.err); } return m; } } }