1 /* 2 * Copyright (c) 2011, 2014, 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.awt.AWTAccessor; 29 import sun.lwawt.macosx.CMenuBar; 30 31 import java.awt.*; 32 import java.awt.event.*; 33 import java.lang.reflect.*; 34 import java.security.*; 35 import java.util.*; 36 37 import javax.swing.*; 38 39 @SuppressWarnings("serial") // JDK implementation class 40 public class ScreenMenuBar extends MenuBar implements ContainerListener, ScreenMenuPropertyHandler, ComponentListener { 41 static boolean sJMenuBarHasHelpMenus = false; //$ could check by calling getHelpMenu in a try block 42 43 JMenuBar fSwingBar; 44 Hashtable<JMenu, ScreenMenu> fSubmenus; 45 46 ScreenMenuPropertyListener fPropertyListener; 47 ScreenMenuPropertyListener fAccessibleListener; 48 49 public ScreenMenuBar(final JMenuBar swingBar) { 50 fSwingBar = swingBar; 51 fSubmenus = new Hashtable<JMenu, ScreenMenu>(fSwingBar.getMenuCount()); 52 } 53 54 public void addNotify() { 55 super.addNotify(); 56 57 fSwingBar.addContainerListener(this); 58 fPropertyListener = new ScreenMenuPropertyListener(this); 59 fSwingBar.addPropertyChangeListener(fPropertyListener); 60 fAccessibleListener = new ScreenMenuPropertyListener(this); 61 fSwingBar.getAccessibleContext().addPropertyChangeListener(fAccessibleListener); 62 63 // We disable component events when the menu bar is not parented. So now we need to 64 // sync back up with the current state of the JMenuBar. We first add the menus we 65 // don't have and then remove the items that are no longer on the JMenuBar. 66 final int count = fSwingBar.getMenuCount(); 67 for(int i = 0; i < count ; i++) { 68 final JMenu m = fSwingBar.getMenu(i); 69 if (m != null) { 70 addSubmenu(m); 71 } 72 } 73 74 final Enumeration<JMenu> e = fSubmenus.keys(); 75 while (e.hasMoreElements()) { 76 final JMenu m = e.nextElement(); 77 if (fSwingBar.getComponentIndex(m) == -1) { 78 removeSubmenu(m); 79 } 80 } 81 } 82 83 public void removeNotify() { 84 // KCH - 3974930 - We do null checks for fSwingBar and fSubmenus because some people are using 85 // reflection to muck about with our ivars 86 if (fSwingBar != null) { 87 fSwingBar.removePropertyChangeListener(fPropertyListener); 88 fSwingBar.getAccessibleContext().removePropertyChangeListener(fAccessibleListener); 89 fSwingBar.removeContainerListener(this); 90 } 91 92 fPropertyListener = null; 93 fAccessibleListener = null; 94 95 if (fSubmenus != null) { 96 // We don't listen to events when the menu bar is not parented. 97 // Remove all the component listeners. 98 final Enumeration<JMenu> e = fSubmenus.keys(); 99 while (e.hasMoreElements()) { 100 final JMenu m = e.nextElement(); 101 m.removeComponentListener(this); 102 } 103 } 104 105 super.removeNotify(); 106 } 107 108 /** 109 * Invoked when a component has been added to the container. 110 */ 111 public void componentAdded(final ContainerEvent e) { 112 final Component child = e.getChild(); 113 if (!(child instanceof JMenu)) return; 114 addSubmenu((JMenu)child); 115 } 116 117 /** 118 * Invoked when a component has been removed from the container. 119 */ 120 public void componentRemoved(final ContainerEvent e) { 121 final Component child = e.getChild(); 122 if (!(child instanceof JMenu)) return; 123 removeSubmenu((JMenu)child); 124 } 125 126 /** 127 * Invoked when the component's size changes. 128 */ 129 public void componentResized(final ComponentEvent e) {} 130 131 /** 132 * Invoked when the component's position changes. 133 */ 134 public void componentMoved(final ComponentEvent e) {} 135 136 /** 137 * Invoked when the component has been made visible. 138 * See componentHidden - we should still have a MenuItem 139 * it just isn't inserted 140 */ 141 public void componentShown(final ComponentEvent e) { 142 final Object source = e.getSource(); 143 if (!(source instanceof JMenuItem)) return; 144 setChildVisible((JMenuItem)source, true); 145 } 146 147 /** 148 * Invoked when the component has been made invisible. 149 * MenuComponent.setVisible does nothing, 150 * so we remove the ScreenMenuItem from the ScreenMenu 151 * but leave it in fItems 152 */ 153 public void componentHidden(final ComponentEvent e) { 154 final Object source = e.getSource(); 155 if (!(source instanceof JMenuItem)) return; 156 setChildVisible((JMenuItem)source, false); 157 } 158 159 /* 160 * MenuComponent.setVisible does nothing, 161 * so we just add or remove the child from the ScreenMenuBar 162 * but leave it in the list 163 */ 164 public void setChildVisible(final JMenuItem child, final boolean b) { 165 if (child instanceof JMenu) { 166 if (b) { 167 addSubmenu((JMenu)child); 168 } else { 169 final ScreenMenu sm = fSubmenus.get(child); 170 if (sm != null) 171 remove(sm); 172 } 173 } 174 } 175 176 public void removeAll() { 177 synchronized (getTreeLock()) { 178 final int nitems = getMenuCount(); 179 for (int i = nitems-1 ; i >= 0 ; i--) { 180 remove(i); 181 } 182 } 183 } 184 185 public void setIcon(final Icon i) {} 186 public void setLabel(final String s) {} 187 188 public void setEnabled(final boolean b) { 189 final int count = fSwingBar.getMenuCount(); 190 for (int i = 0; i < count; i++) { 191 fSwingBar.getMenu(i).setEnabled(b); 192 } 193 } 194 195 public void setAccelerator(final KeyStroke ks) {} 196 public void setToolTipText(final String tooltip) {} 197 198 // only check and radio items can be indeterminate 199 public void setIndeterminate(boolean indeterminate) { } 200 201 ScreenMenu addSubmenu(final JMenu m) { 202 ScreenMenu sm = fSubmenus.get(m); 203 204 if (sm == null) { 205 sm = new ScreenMenu(m); 206 m.addComponentListener(this); 207 fSubmenus.put(m, sm); 208 } 209 210 sm.setEnabled(m.isEnabled()); 211 212 // MenuComponents don't support setVisible, so we just don't add it to the menubar 213 if (m.isVisible() && sm.getParent() == null) { 214 int newIndex = 0, currVisibleIndex = 0; 215 JMenu menu = null; 216 final int menuCount = fSwingBar.getMenuCount(); 217 for (int i = 0; i < menuCount; i++) { 218 menu = fSwingBar.getMenu(i); 219 if (menu == m) { 220 newIndex = currVisibleIndex; 221 break; 222 } 223 if (menu != null && menu.isVisible()) { 224 currVisibleIndex++; 225 } 226 } 227 add(sm, newIndex); 228 } 229 230 return sm; 231 } 232 233 /** 234 * Remove the screen menu associated with the specifiec menu. This 235 * also removes any associated component listener on the screen menu 236 * and removes the key/value (menu/screen menu) from the fSubmenus cache. 237 * 238 * @param menu The swing menu we want to remove the screen menu for. 239 */ 240 private void removeSubmenu(final JMenu menu) { 241 final ScreenMenu screenMenu = fSubmenus.get(menu); 242 if (screenMenu == null) return; 243 244 menu.removeComponentListener(this); 245 remove(screenMenu); 246 fSubmenus.remove(menu); 247 } 248 249 @SuppressWarnings("deprecation") 250 public Menu add(final Menu m, final int index) { 251 synchronized (getTreeLock()) { 252 if (m.getParent() != null) { 253 m.getParent().remove(m); 254 } 255 256 final Vector<Menu> menus = AWTAccessor.getMenuBarAccessor().getMenus(this); 257 menus.insertElementAt(m, index); 258 AWTAccessor.getMenuComponentAccessor().setParent(m, this); 259 260 final CMenuBar peer = (CMenuBar)getPeer(); 261 if (peer == null) return m; 262 263 peer.setNextInsertionIndex(index); 264 if (m.getPeer() == null) { 265 m.addNotify(); 266 } 267 268 peer.setNextInsertionIndex(-1); 269 return m; 270 } 271 } 272 }