1 /* 2 * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.apple.laf; 27 28 import sun.lwawt.macosx.CMenuBar; 29 30 import java.awt.*; 31 import java.awt.event.*; 32 import java.util.*; 33 34 import javax.swing.*; 35 36 import static sun.awt.AWTAccessor.*; 37 38 @SuppressWarnings("serial") // JDK implementation class 39 public class ScreenMenuBar extends MenuBar 40 implements ContainerListener, ScreenMenuPropertyHandler, 41 ComponentListener { 42 43 static boolean sJMenuBarHasHelpMenus = false; //$ could check by calling getHelpMenu in a try block 44 45 JMenuBar fSwingBar; 46 Hashtable<JMenu, ScreenMenu> fSubmenus; 47 48 ScreenMenuPropertyListener fPropertyListener; 49 ScreenMenuPropertyListener fAccessibleListener; 50 ScreenMenuBar(final JMenuBar swingBar)51 public ScreenMenuBar(final JMenuBar swingBar) { 52 fSwingBar = swingBar; 53 fSubmenus = new Hashtable<JMenu, ScreenMenu>(fSwingBar.getMenuCount()); 54 } 55 addNotify()56 public void addNotify() { 57 super.addNotify(); 58 59 fSwingBar.addContainerListener(this); 60 fPropertyListener = new ScreenMenuPropertyListener(this); 61 fSwingBar.addPropertyChangeListener(fPropertyListener); 62 fAccessibleListener = new ScreenMenuPropertyListener(this); 63 fSwingBar.getAccessibleContext().addPropertyChangeListener(fAccessibleListener); 64 65 // We disable component events when the menu bar is not parented. So now we need to 66 // sync back up with the current state of the JMenuBar. We first add the menus we 67 // don't have and then remove the items that are no longer on the JMenuBar. 68 final int count = fSwingBar.getMenuCount(); 69 for(int i = 0; i < count ; i++) { 70 final JMenu m = fSwingBar.getMenu(i); 71 if (m != null) { 72 addSubmenu(m); 73 } 74 } 75 76 final Enumeration<JMenu> e = fSubmenus.keys(); 77 while (e.hasMoreElements()) { 78 final JMenu m = e.nextElement(); 79 if (fSwingBar.getComponentIndex(m) == -1) { 80 removeSubmenu(m); 81 } 82 } 83 } 84 removeNotify()85 public void removeNotify() { 86 // KCH - 3974930 - We do null checks for fSwingBar and fSubmenus because some people are using 87 // reflection to muck about with our ivars 88 if (fSwingBar != null) { 89 fSwingBar.removePropertyChangeListener(fPropertyListener); 90 fSwingBar.getAccessibleContext().removePropertyChangeListener(fAccessibleListener); 91 fSwingBar.removeContainerListener(this); 92 } 93 94 fPropertyListener = null; 95 fAccessibleListener = null; 96 97 if (fSubmenus != null) { 98 // We don't listen to events when the menu bar is not parented. 99 // Remove all the component listeners. 100 final Enumeration<JMenu> e = fSubmenus.keys(); 101 while (e.hasMoreElements()) { 102 final JMenu m = e.nextElement(); 103 m.removeComponentListener(this); 104 } 105 } 106 107 super.removeNotify(); 108 } 109 110 /** 111 * Invoked when a component has been added to the container. 112 */ componentAdded(final ContainerEvent e)113 public void componentAdded(final ContainerEvent e) { 114 final Component child = e.getChild(); 115 if (!(child instanceof JMenu)) return; 116 addSubmenu((JMenu)child); 117 } 118 119 /** 120 * Invoked when a component has been removed from the container. 121 */ componentRemoved(final ContainerEvent e)122 public void componentRemoved(final ContainerEvent e) { 123 final Component child = e.getChild(); 124 if (!(child instanceof JMenu)) return; 125 removeSubmenu((JMenu)child); 126 } 127 128 /** 129 * Invoked when the component's size changes. 130 */ componentResized(final ComponentEvent e)131 public void componentResized(final ComponentEvent e) {} 132 133 /** 134 * Invoked when the component's position changes. 135 */ componentMoved(final ComponentEvent e)136 public void componentMoved(final ComponentEvent e) {} 137 138 /** 139 * Invoked when the component has been made visible. 140 * See componentHidden - we should still have a MenuItem 141 * it just isn't inserted 142 */ componentShown(final ComponentEvent e)143 public void componentShown(final ComponentEvent e) { 144 final Object source = e.getSource(); 145 if (!(source instanceof JMenuItem)) return; 146 setChildVisible((JMenuItem)source, true); 147 } 148 149 /** 150 * Invoked when the component has been made invisible. 151 * MenuComponent.setVisible does nothing, 152 * so we remove the ScreenMenuItem from the ScreenMenu 153 * but leave it in fItems 154 */ componentHidden(final ComponentEvent e)155 public void componentHidden(final ComponentEvent e) { 156 final Object source = e.getSource(); 157 if (!(source instanceof JMenuItem)) return; 158 setChildVisible((JMenuItem)source, false); 159 } 160 161 /* 162 * MenuComponent.setVisible does nothing, 163 * so we just add or remove the child from the ScreenMenuBar 164 * but leave it in the list 165 */ setChildVisible(final JMenuItem child, final boolean b)166 public void setChildVisible(final JMenuItem child, final boolean b) { 167 if (child instanceof JMenu) { 168 if (b) { 169 addSubmenu((JMenu)child); 170 } else { 171 final ScreenMenu sm = fSubmenus.get(child); 172 if (sm != null) 173 remove(sm); 174 } 175 } 176 } 177 removeAll()178 public void removeAll() { 179 synchronized (getTreeLock()) { 180 final int nitems = getMenuCount(); 181 for (int i = nitems-1 ; i >= 0 ; i--) { 182 remove(i); 183 } 184 } 185 } 186 setIcon(final Icon i)187 public void setIcon(final Icon i) {} setLabel(final String s)188 public void setLabel(final String s) {} 189 setEnabled(final boolean b)190 public void setEnabled(final boolean b) { 191 final int count = fSwingBar.getMenuCount(); 192 for (int i = 0; i < count; i++) { 193 fSwingBar.getMenu(i).setEnabled(b); 194 } 195 } 196 setAccelerator(final KeyStroke ks)197 public void setAccelerator(final KeyStroke ks) {} setToolTipText(final String tooltip)198 public void setToolTipText(final String tooltip) {} 199 200 // only check and radio items can be indeterminate setIndeterminate(boolean indeterminate)201 public void setIndeterminate(boolean indeterminate) { } 202 addSubmenu(final JMenu m)203 ScreenMenu addSubmenu(final JMenu m) { 204 ScreenMenu sm = fSubmenus.get(m); 205 206 if (sm == null) { 207 sm = new ScreenMenu(m); 208 m.addComponentListener(this); 209 fSubmenus.put(m, sm); 210 } 211 212 sm.setEnabled(m.isEnabled()); 213 214 // MenuComponents don't support setVisible, so we just don't add it to the menubar 215 if (m.isVisible() && sm.getParent() == null) { 216 int newIndex = 0, currVisibleIndex = 0; 217 JMenu menu = null; 218 final int menuCount = fSwingBar.getMenuCount(); 219 for (int i = 0; i < menuCount; i++) { 220 menu = fSwingBar.getMenu(i); 221 if (menu == m) { 222 newIndex = currVisibleIndex; 223 break; 224 } 225 if (menu != null && menu.isVisible()) { 226 currVisibleIndex++; 227 } 228 } 229 add(sm, newIndex); 230 } 231 232 return sm; 233 } 234 235 /** 236 * Remove the screen menu associated with the specifiec menu. This 237 * also removes any associated component listener on the screen menu 238 * and removes the key/value (menu/screen menu) from the fSubmenus cache. 239 * 240 * @param menu The swing menu we want to remove the screen menu for. 241 */ removeSubmenu(final JMenu menu)242 private void removeSubmenu(final JMenu menu) { 243 final ScreenMenu screenMenu = fSubmenus.get(menu); 244 if (screenMenu == null) return; 245 246 menu.removeComponentListener(this); 247 remove(screenMenu); 248 fSubmenus.remove(menu); 249 } 250 add(final Menu m, final int index)251 public Menu add(final Menu m, final int index) { 252 synchronized (getTreeLock()) { 253 if (m.getParent() != null) { 254 m.getParent().remove(m); 255 } 256 257 final Vector<Menu> menus = getMenuBarAccessor().getMenus(this); 258 menus.insertElementAt(m, index); 259 final MenuComponentAccessor acc = getMenuComponentAccessor(); 260 acc.setParent(m, this); 261 262 final CMenuBar peer = acc.getPeer(this); 263 if (peer == null) return m; 264 265 peer.setNextInsertionIndex(index); 266 final CMenuBar mPeer = acc.getPeer(m); 267 if (mPeer == null) { 268 m.addNotify(); 269 } 270 271 peer.setNextInsertionIndex(-1); 272 return m; 273 } 274 } 275 } 276