1 /* 2 * Copyright (c) 1996, 2016, 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 java.awt; 27 28 import java.awt.peer.PopupMenuPeer; 29 30 import javax.accessibility.AccessibleContext; 31 import javax.accessibility.AccessibleRole; 32 33 import sun.awt.AWTAccessor; 34 35 /** 36 * A class that implements a menu which can be dynamically popped up 37 * at a specified position within a component. 38 * <p> 39 * As the inheritance hierarchy implies, a {@code PopupMenu} 40 * can be used anywhere a {@code Menu} can be used. 41 * However, if you use a {@code PopupMenu} like a {@code Menu} 42 * (e.g., you add it to a {@code MenuBar}), then you <b>cannot</b> 43 * call {@code show} on that {@code PopupMenu}. 44 * 45 * @author Amy Fowler 46 */ 47 public class PopupMenu extends Menu { 48 49 private static final String base = "popup"; 50 static int nameCounter = 0; 51 52 transient volatile boolean isTrayIconPopup; 53 54 static { 55 AWTAccessor.setPopupMenuAccessor( 56 new AWTAccessor.PopupMenuAccessor() { 57 public boolean isTrayIconPopup(PopupMenu popupMenu) { 58 return popupMenu.isTrayIconPopup; 59 } 60 }); 61 } 62 63 /* 64 * JDK 1.1 serialVersionUID 65 */ 66 private static final long serialVersionUID = -4620452533522760060L; 67 68 /** 69 * Creates a new popup menu with an empty name. 70 * @exception HeadlessException if GraphicsEnvironment.isHeadless() 71 * returns true. 72 * @see java.awt.GraphicsEnvironment#isHeadless 73 */ 74 public PopupMenu() throws HeadlessException { 75 this(""); 76 } 77 78 /** 79 * Creates a new popup menu with the specified name. 80 * 81 * @param label a non-{@code null} string specifying 82 * the popup menu's label 83 * @exception HeadlessException if GraphicsEnvironment.isHeadless() 84 * returns true. 85 * @see java.awt.GraphicsEnvironment#isHeadless 86 */ 87 public PopupMenu(String label) throws HeadlessException { 88 super(label); 89 } 90 91 /** 92 * {@inheritDoc} 93 */ 94 public MenuContainer getParent() { 95 if (isTrayIconPopup) { 96 return null; 97 } 98 return super.getParent(); 99 } 100 101 /** 102 * Constructs a name for this {@code MenuComponent}. 103 * Called by {@code getName} when the name is {@code null}. 104 */ 105 String constructComponentName() { 106 synchronized (PopupMenu.class) { 107 return base + nameCounter++; 108 } 109 } 110 111 /** 112 * Creates the popup menu's peer. 113 * The peer allows us to change the appearance of the popup menu without 114 * changing any of the popup menu's functionality. 115 */ 116 public void addNotify() { 117 synchronized (getTreeLock()) { 118 // If our parent is not a Component, then this PopupMenu is 119 // really just a plain, old Menu. 120 if (parent != null && !(parent instanceof Component)) { 121 super.addNotify(); 122 } 123 else { 124 if (peer == null) 125 peer = getComponentFactory().createPopupMenu(this); 126 int nitems = getItemCount(); 127 for (int i = 0 ; i < nitems ; i++) { 128 MenuItem mi = getItem(i); 129 mi.parent = this; 130 mi.addNotify(); 131 } 132 } 133 } 134 } 135 136 /** 137 * Shows the popup menu at the x, y position relative to an origin 138 * component. 139 * The origin component must be contained within the component 140 * hierarchy of the popup menu's parent. Both the origin and the parent 141 * must be showing on the screen for this method to be valid. 142 * <p> 143 * If this {@code PopupMenu} is being used as a {@code Menu} 144 * (i.e., it has a non-{@code Component} parent), 145 * then you cannot call this method on the {@code PopupMenu}. 146 * 147 * @param origin the component which defines the coordinate space 148 * @param x the x coordinate position to popup the menu 149 * @param y the y coordinate position to popup the menu 150 * @exception NullPointerException if the parent is {@code null} 151 * @exception IllegalArgumentException if this {@code PopupMenu} 152 * has a non-{@code Component} parent 153 * @exception IllegalArgumentException if the origin is not in the 154 * parent's hierarchy 155 * @exception RuntimeException if the parent is not showing on screen 156 */ 157 public void show(Component origin, int x, int y) { 158 // Use localParent for thread safety. 159 MenuContainer localParent = parent; 160 if (localParent == null) { 161 throw new NullPointerException("parent is null"); 162 } 163 if (!(localParent instanceof Component)) { 164 throw new IllegalArgumentException( 165 "PopupMenus with non-Component parents cannot be shown"); 166 } 167 Component compParent = (Component)localParent; 168 //Fixed 6278745: Incorrect exception throwing in PopupMenu.show() method 169 //Exception was not thrown if compParent was not equal to origin and 170 //was not Container 171 if (compParent != origin) { 172 if (compParent instanceof Container) { 173 if (!((Container)compParent).isAncestorOf(origin)) { 174 throw new IllegalArgumentException("origin not in parent's hierarchy"); 175 } 176 } else { 177 throw new IllegalArgumentException("origin not in parent's hierarchy"); 178 } 179 } 180 if (compParent.peer == null || !compParent.isShowing()) { 181 throw new RuntimeException("parent not showing on screen"); 182 } 183 if (peer == null) { 184 addNotify(); 185 } 186 synchronized (getTreeLock()) { 187 if (peer != null) { 188 ((PopupMenuPeer)peer).show( 189 new Event(origin, 0, Event.MOUSE_DOWN, x, y, 0, 0)); 190 } 191 } 192 } 193 194 195 ///////////////// 196 // Accessibility support 197 //////////////// 198 199 /** 200 * Gets the {@code AccessibleContext} associated with this 201 * {@code PopupMenu}. 202 * 203 * @return the {@code AccessibleContext} of this 204 * {@code PopupMenu} 205 * @since 1.3 206 */ 207 public AccessibleContext getAccessibleContext() { 208 if (accessibleContext == null) { 209 accessibleContext = new AccessibleAWTPopupMenu(); 210 } 211 return accessibleContext; 212 } 213 214 /** 215 * Inner class of PopupMenu used to provide default support for 216 * accessibility. This class is not meant to be used directly by 217 * application developers, but is instead meant only to be 218 * subclassed by menu component developers. 219 * <p> 220 * The class used to obtain the accessible role for this object. 221 * @since 1.3 222 */ 223 protected class AccessibleAWTPopupMenu extends AccessibleAWTMenu 224 { 225 /* 226 * JDK 1.3 serialVersionUID 227 */ 228 private static final long serialVersionUID = -4282044795947239955L; 229 230 /** 231 * Get the role of this object. 232 * 233 * @return an instance of AccessibleRole describing the role of the 234 * object 235 */ 236 public AccessibleRole getAccessibleRole() { 237 return AccessibleRole.POPUP_MENU; 238 } 239 240 } // class AccessibleAWTPopupMenu 241 242 }