Path: blob/master/src/java.desktop/share/classes/java/awt/Choice.java
41152 views
/*1* Copyright (c) 1995, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.awt;2627import java.awt.event.ItemEvent;28import java.awt.event.ItemListener;29import java.awt.peer.ChoicePeer;30import java.io.IOException;31import java.io.ObjectInputStream;32import java.io.ObjectOutputStream;33import java.io.Serial;34import java.util.EventListener;35import java.util.Vector;3637import javax.accessibility.Accessible;38import javax.accessibility.AccessibleAction;39import javax.accessibility.AccessibleContext;40import javax.accessibility.AccessibleRole;4142/**43* The {@code Choice} class presents a pop-up menu of choices.44* The current choice is displayed as the title of the menu.45* <p>46* The following code example produces a pop-up menu:47*48* <hr><blockquote><pre>49* Choice ColorChooser = new Choice();50* ColorChooser.add("Green");51* ColorChooser.add("Red");52* ColorChooser.add("Blue");53* </pre></blockquote><hr>54* <p>55* After this choice menu has been added to a panel,56* it appears as follows in its normal state:57* <p>58* <img src="doc-files/Choice-1.gif" alt="The following text describes the59* graphic" style="margin: 7px 10px;">60* <p>61* In the picture, {@code "Green"} is the current choice.62* Pushing the mouse button down on the object causes a menu to63* appear with the current choice highlighted.64* <p>65* Some native platforms do not support arbitrary resizing of66* {@code Choice} components and the behavior of67* {@code setSize()/getSize()} is bound by68* such limitations.69* Native GUI {@code Choice} components' size are often bound by such70* attributes as font size and length of items contained within71* the {@code Choice}.72*73* @author Sami Shaio74* @author Arthur van Hoff75* @since 1.076*/77public class Choice extends Component implements ItemSelectable, Accessible {78/**79* The items for the {@code Choice}.80* This can be a {@code null} value.81* @serial82* @see #add(String)83* @see #addItem(String)84* @see #getItem(int)85* @see #getItemCount()86* @see #insert(String, int)87* @see #remove(String)88*/89Vector<String> pItems;9091/**92* The index of the current choice for this {@code Choice}93* or -1 if nothing is selected.94* @serial95* @see #getSelectedItem()96* @see #select(int)97*/98int selectedIndex = -1;99100transient ItemListener itemListener;101102private static final String base = "choice";103private static int nameCounter = 0;104105/**106* Use serialVersionUID from JDK 1.1 for interoperability.107*/108@Serial109private static final long serialVersionUID = -4075310674757313071L;110111static {112/* ensure that the necessary native libraries are loaded */113Toolkit.loadLibraries();114/* initialize JNI field and method ids */115if (!GraphicsEnvironment.isHeadless()) {116initIDs();117}118}119120/**121* Creates a new choice menu. The menu initially has no items in it.122* <p>123* By default, the first item added to the choice menu becomes the124* selected item, until a different selection is made by the user125* by calling one of the {@code select} methods.126* @exception HeadlessException if GraphicsEnvironment.isHeadless()127* returns true128* @see java.awt.GraphicsEnvironment#isHeadless129* @see #select(int)130* @see #select(java.lang.String)131*/132public Choice() throws HeadlessException {133GraphicsEnvironment.checkHeadless();134pItems = new Vector<>();135}136137/**138* Constructs a name for this component. Called by139* {@code getName} when the name is {@code null}.140*/141String constructComponentName() {142synchronized (Choice.class) {143return base + nameCounter++;144}145}146147/**148* Creates the {@code Choice}'s peer. This peer allows us149* to change the look150* of the {@code Choice} without changing its functionality.151* @see java.awt.Component#getToolkit()152*/153public void addNotify() {154synchronized (getTreeLock()) {155if (peer == null)156peer = getComponentFactory().createChoice(this);157super.addNotify();158}159}160161/**162* Returns the number of items in this {@code Choice} menu.163*164* @return the number of items in this {@code Choice} menu165* @see #getItem166* @since 1.1167*/168public int getItemCount() {169return countItems();170}171172/**173* Returns the number of items in this {@code Choice} menu.174*175* @return the number of items in this {@code Choice} menu176* @deprecated As of JDK version 1.1,177* replaced by {@code getItemCount()}.178*/179@Deprecated180public int countItems() {181return pItems.size();182}183184/**185* Gets the string at the specified index in this186* {@code Choice} menu.187*188* @param index the index at which to begin189* @return the item at the specified index190* @see #getItemCount191*/192public String getItem(int index) {193return getItemImpl(index);194}195196/*197* This is called by the native code, so client code can't198* be called on the toolkit thread.199*/200final String getItemImpl(int index) {201return pItems.elementAt(index);202}203204/**205* Adds an item to this {@code Choice} menu.206* @param item the item to be added207* @exception NullPointerException if the item's value is208* {@code null}209* @since 1.1210*/211public void add(String item) {212addItem(item);213}214215/**216* Obsolete as of Java 2 platform v1.1. Please use the217* {@code add} method instead.218* <p>219* Adds an item to this {@code Choice} menu.220* @param item the item to be added221* @exception NullPointerException if the item's value is equal to222* {@code null}223*/224public void addItem(String item) {225synchronized (this) {226insertNoInvalidate(item, pItems.size());227}228229// This could change the preferred size of the Component.230invalidateIfValid();231}232233/**234* Inserts an item to this {@code Choice},235* but does not invalidate the {@code Choice}.236* Client methods must provide their own synchronization before237* invoking this method.238* @param item the item to be added239* @param index the new item position240* @exception NullPointerException if the item's value is equal to241* {@code null}242*/243private void insertNoInvalidate(String item, int index) {244if (item == null) {245throw new246NullPointerException("cannot add null item to Choice");247}248pItems.insertElementAt(item, index);249ChoicePeer peer = (ChoicePeer)this.peer;250if (peer != null) {251peer.add(item, index);252}253// no selection or selection shifted up254if (selectedIndex < 0 || selectedIndex >= index) {255select(0);256}257}258259260/**261* Inserts the item into this choice at the specified position.262* Existing items at an index greater than or equal to263* {@code index} are shifted up by one to accommodate264* the new item. If {@code index} is greater than or265* equal to the number of items in this choice,266* {@code item} is added to the end of this choice.267* <p>268* If the item is the first one being added to the choice,269* then the item becomes selected. Otherwise, if the270* selected item was one of the items shifted, the first271* item in the choice becomes the selected item. If the272* selected item was no among those shifted, it remains273* the selected item.274* @param item the non-{@code null} item to be inserted275* @param index the position at which the item should be inserted276* @exception IllegalArgumentException if index is less than 0277*/278public void insert(String item, int index) {279synchronized (this) {280if (index < 0) {281throw new IllegalArgumentException("index less than zero.");282}283/* if the index greater than item count, add item to the end */284index = Math.min(index, pItems.size());285286insertNoInvalidate(item, index);287}288289// This could change the preferred size of the Component.290invalidateIfValid();291}292293/**294* Removes the first occurrence of {@code item}295* from the {@code Choice} menu. If the item296* being removed is the currently selected item,297* then the first item in the choice becomes the298* selected item. Otherwise, the currently selected299* item remains selected (and the selected index is300* updated accordingly).301* @param item the item to remove from this {@code Choice} menu302* @exception IllegalArgumentException if the item doesn't303* exist in the choice menu304* @since 1.1305*/306public void remove(String item) {307synchronized (this) {308int index = pItems.indexOf(item);309if (index < 0) {310throw new IllegalArgumentException("item " + item +311" not found in choice");312} else {313removeNoInvalidate(index);314}315}316317// This could change the preferred size of the Component.318invalidateIfValid();319}320321/**322* Removes an item from the choice menu323* at the specified position. If the item324* being removed is the currently selected item,325* then the first item in the choice becomes the326* selected item. Otherwise, the currently selected327* item remains selected (and the selected index is328* updated accordingly).329* @param position the position of the item330* @throws IndexOutOfBoundsException if the specified331* position is out of bounds332* @since 1.1333*/334public void remove(int position) {335synchronized (this) {336removeNoInvalidate(position);337}338339// This could change the preferred size of the Component.340invalidateIfValid();341}342343/**344* Removes an item from the {@code Choice} at the345* specified position, but does not invalidate the {@code Choice}.346* Client methods must provide their347* own synchronization before invoking this method.348* @param position the position of the item349*/350private void removeNoInvalidate(int position) {351pItems.removeElementAt(position);352ChoicePeer peer = (ChoicePeer)this.peer;353if (peer != null) {354peer.remove(position);355}356/* Adjust selectedIndex if selected item was removed. */357if (pItems.size() == 0) {358selectedIndex = -1;359} else if (selectedIndex == position) {360select(0);361} else if (selectedIndex > position) {362select(selectedIndex-1);363}364}365366367/**368* Removes all items from the choice menu.369* @see #remove370* @since 1.1371*/372public void removeAll() {373synchronized (this) {374if (peer != null) {375((ChoicePeer)peer).removeAll();376}377pItems.removeAllElements();378selectedIndex = -1;379}380381// This could change the preferred size of the Component.382invalidateIfValid();383}384385/**386* Gets a representation of the current choice as a string.387* @return a string representation of the currently388* selected item in this choice menu389* @see #getSelectedIndex390*/391public synchronized String getSelectedItem() {392return (selectedIndex >= 0) ? getItem(selectedIndex) : null;393}394395/**396* Returns an array (length 1) containing the currently selected397* item. If this choice has no items, returns {@code null}.398* @see ItemSelectable399*/400public synchronized Object[] getSelectedObjects() {401if (selectedIndex >= 0) {402Object[] items = new Object[1];403items[0] = getItem(selectedIndex);404return items;405}406return null;407}408409/**410* Returns the index of the currently selected item.411* If nothing is selected, returns -1.412*413* @return the index of the currently selected item, or -1 if nothing414* is currently selected415* @see #getSelectedItem416*/417public int getSelectedIndex() {418return selectedIndex;419}420421/**422* Sets the selected item in this {@code Choice} menu to be the423* item at the specified position.424*425* <p>Note that this method should be primarily used to426* initially select an item in this component.427* Programmatically calling this method will <i>not</i> trigger428* an {@code ItemEvent}. The only way to trigger an429* {@code ItemEvent} is by user interaction.430*431* @param pos the position of the selected item432* @exception IllegalArgumentException if the specified433* position is greater than the434* number of items or less than zero435* @see #getSelectedItem436* @see #getSelectedIndex437*/438public synchronized void select(int pos) {439if ((pos >= pItems.size()) || (pos < 0)) {440throw new IllegalArgumentException("illegal Choice item position: " + pos);441}442if (pItems.size() > 0) {443selectedIndex = pos;444ChoicePeer peer = (ChoicePeer)this.peer;445if (peer != null) {446peer.select(pos);447}448}449}450451/**452* Sets the selected item in this {@code Choice} menu453* to be the item whose name is equal to the specified string.454* If more than one item matches (is equal to) the specified string,455* the one with the smallest index is selected.456*457* <p>Note that this method should be primarily used to458* initially select an item in this component.459* Programmatically calling this method will <i>not</i> trigger460* an {@code ItemEvent}. The only way to trigger an461* {@code ItemEvent} is by user interaction.462*463* @param str the specified string464* @see #getSelectedItem465* @see #getSelectedIndex466*/467public synchronized void select(String str) {468int index = pItems.indexOf(str);469if (index >= 0) {470select(index);471}472}473474/**475* Adds the specified item listener to receive item events from476* this {@code Choice} menu. Item events are sent in response477* to user input, but not in response to calls to {@code select}.478* If l is {@code null}, no exception is thrown and no action479* is performed.480* <p>Refer to <a href="doc-files/AWTThreadIssues.html#ListenersThreads"481* >AWT Threading Issues</a> for details on AWT's threading model.482* @param l the item listener483* @see #removeItemListener484* @see #getItemListeners485* @see #select486* @see java.awt.event.ItemEvent487* @see java.awt.event.ItemListener488* @since 1.1489*/490public synchronized void addItemListener(ItemListener l) {491if (l == null) {492return;493}494itemListener = AWTEventMulticaster.add(itemListener, l);495newEventsOnly = true;496}497498/**499* Removes the specified item listener so that it no longer receives500* item events from this {@code Choice} menu.501* If l is {@code null}, no exception is thrown and no502* action is performed.503* <p>Refer to <a href="doc-files/AWTThreadIssues.html#ListenersThreads"504* >AWT Threading Issues</a> for details on AWT's threading model.505* @param l the item listener506* @see #addItemListener507* @see #getItemListeners508* @see java.awt.event.ItemEvent509* @see java.awt.event.ItemListener510* @since 1.1511*/512public synchronized void removeItemListener(ItemListener l) {513if (l == null) {514return;515}516itemListener = AWTEventMulticaster.remove(itemListener, l);517}518519/**520* Returns an array of all the item listeners521* registered on this choice.522*523* @return all of this choice's {@code ItemListener}s524* or an empty array if no item525* listeners are currently registered526*527* @see #addItemListener528* @see #removeItemListener529* @see java.awt.event.ItemEvent530* @see java.awt.event.ItemListener531* @since 1.4532*/533public synchronized ItemListener[] getItemListeners() {534return getListeners(ItemListener.class);535}536537/**538* Returns an array of all the objects currently registered539* as <code><em>Foo</em>Listener</code>s540* upon this {@code Choice}.541* <code><em>Foo</em>Listener</code>s are registered using the542* <code>add<em>Foo</em>Listener</code> method.543*544* <p>545* You can specify the {@code listenerType} argument546* with a class literal, such as547* <code><em>Foo</em>Listener.class</code>.548* For example, you can query a549* {@code Choice c}550* for its item listeners with the following code:551*552* <pre>ItemListener[] ils = (ItemListener[])(c.getListeners(ItemListener.class));</pre>553*554* If no such listeners exist, this method returns an empty array.555*556* @param listenerType the type of listeners requested; this parameter557* should specify an interface that descends from558* {@code java.util.EventListener}559* @return an array of all objects registered as560* <code><em>Foo</em>Listener</code>s on this choice,561* or an empty array if no such562* listeners have been added563* @exception ClassCastException if {@code listenerType}564* doesn't specify a class or interface that implements565* {@code java.util.EventListener}566*567* @see #getItemListeners568* @since 1.3569*/570public <T extends EventListener> T[] getListeners(Class<T> listenerType) {571EventListener l = null;572if (listenerType == ItemListener.class) {573l = itemListener;574} else {575return super.getListeners(listenerType);576}577return AWTEventMulticaster.getListeners(l, listenerType);578}579580// REMIND: remove when filtering is done at lower level581boolean eventEnabled(AWTEvent e) {582if (e.id == ItemEvent.ITEM_STATE_CHANGED) {583if ((eventMask & AWTEvent.ITEM_EVENT_MASK) != 0 ||584itemListener != null) {585return true;586}587return false;588}589return super.eventEnabled(e);590}591592/**593* Processes events on this choice. If the event is an594* instance of {@code ItemEvent}, it invokes the595* {@code processItemEvent} method. Otherwise, it calls its596* superclass's {@code processEvent} method.597* <p>Note that if the event parameter is {@code null}598* the behavior is unspecified and may result in an599* exception.600*601* @param e the event602* @see java.awt.event.ItemEvent603* @see #processItemEvent604* @since 1.1605*/606protected void processEvent(AWTEvent e) {607if (e instanceof ItemEvent) {608processItemEvent((ItemEvent)e);609return;610}611super.processEvent(e);612}613614/**615* Processes item events occurring on this {@code Choice}616* menu by dispatching them to any registered617* {@code ItemListener} objects.618* <p>619* This method is not called unless item events are620* enabled for this component. Item events are enabled621* when one of the following occurs:622* <ul>623* <li>An {@code ItemListener} object is registered624* via {@code addItemListener}.625* <li>Item events are enabled via {@code enableEvents}.626* </ul>627* <p>Note that if the event parameter is {@code null}628* the behavior is unspecified and may result in an629* exception.630*631* @param e the item event632* @see java.awt.event.ItemEvent633* @see java.awt.event.ItemListener634* @see #addItemListener(ItemListener)635* @see java.awt.Component#enableEvents636* @since 1.1637*/638protected void processItemEvent(ItemEvent e) {639ItemListener listener = itemListener;640if (listener != null) {641listener.itemStateChanged(e);642}643}644645/**646* Returns a string representing the state of this {@code Choice}647* menu. This method is intended to be used only for debugging purposes,648* and the content and format of the returned string may vary between649* implementations. The returned string may be empty but may not be650* {@code null}.651*652* @return the parameter string of this {@code Choice} menu653*/654protected String paramString() {655return super.paramString() + ",current=" + getSelectedItem();656}657658659/* Serialization support.660*/661662/**663* Serialized data version.664* @serial665*/666private int choiceSerializedDataVersion = 1;667668/**669* Writes default serializable fields to stream. Writes670* a list of serializable {@code ItemListeners}671* as optional data. The non-serializable672* {@code ItemListeners} are detected and673* no attempt is made to serialize them.674*675* @param s the {@code ObjectOutputStream} to write676* @throws IOException if an I/O error occurs677* @serialData {@code null} terminated sequence of 0678* or more pairs; the pair consists of a {@code String}679* and an {@code Object}; the {@code String} indicates680* the type of object and is one of the following:681* {@code itemListenerK} indicating an682* {@code ItemListener} object683*684* @see AWTEventMulticaster#save(ObjectOutputStream, String, EventListener)685* @see java.awt.Component#itemListenerK686* @see #readObject(ObjectInputStream)687*/688@Serial689private void writeObject(ObjectOutputStream s)690throws java.io.IOException691{692s.defaultWriteObject();693694AWTEventMulticaster.save(s, itemListenerK, itemListener);695s.writeObject(null);696}697698/**699* Reads the {@code ObjectInputStream} and if it700* isn't {@code null} adds a listener to receive701* item events fired by the {@code Choice} item.702* Unrecognized keys or values will be ignored.703*704* @param s the {@code ObjectInputStream} to read705* @throws ClassNotFoundException if the class of a serialized object could706* not be found707* @throws IOException if an I/O error occurs708* @throws HeadlessException if {@code GraphicsEnvironment.isHeadless()}709* returns {@code true}710* @serial711* @see #removeItemListener(ItemListener)712* @see #addItemListener(ItemListener)713* @see java.awt.GraphicsEnvironment#isHeadless714* @see #writeObject(ObjectOutputStream)715*/716@Serial717private void readObject(ObjectInputStream s)718throws ClassNotFoundException, IOException, HeadlessException719{720GraphicsEnvironment.checkHeadless();721s.defaultReadObject();722723Object keyOrNull;724while(null != (keyOrNull = s.readObject())) {725String key = ((String)keyOrNull).intern();726727if (itemListenerK == key)728addItemListener((ItemListener)(s.readObject()));729730else // skip value for unrecognized key731s.readObject();732}733}734735/**736* Initialize JNI field and method IDs737*/738private static native void initIDs();739740/////////////////741// Accessibility support742////////////////743744745/**746* Gets the {@code AccessibleContext} associated with this747* {@code Choice}. For {@code Choice} components,748* the {@code AccessibleContext} takes the form of an749* {@code AccessibleAWTChoice}. A new {@code AccessibleAWTChoice}750* instance is created if necessary.751*752* @return an {@code AccessibleAWTChoice} that serves as the753* {@code AccessibleContext} of this {@code Choice}754* @since 1.3755*/756public AccessibleContext getAccessibleContext() {757if (accessibleContext == null) {758accessibleContext = new AccessibleAWTChoice();759}760return accessibleContext;761}762763/**764* This class implements accessibility support for the765* {@code Choice} class. It provides an implementation of the766* Java Accessibility API appropriate to choice user-interface elements.767* @since 1.3768*/769protected class AccessibleAWTChoice extends AccessibleAWTComponent770implements AccessibleAction771{772/**773* Use serialVersionUID from JDK 1.3 for interoperability.774*/775@Serial776private static final long serialVersionUID = 7175603582428509322L;777778/**779* Constructor for {@code AccessibleAWTChoice}780*/781public AccessibleAWTChoice() {782super();783}784785/**786* Get the AccessibleAction associated with this object. In the787* implementation of the Java Accessibility API for this class,788* return this object, which is responsible for implementing the789* AccessibleAction interface on behalf of itself.790*791* @return this object792* @see AccessibleAction793*/794public AccessibleAction getAccessibleAction() {795return this;796}797798/**799* Get the role of this object.800*801* @return an instance of AccessibleRole describing the role of the802* object803* @see AccessibleRole804*/805public AccessibleRole getAccessibleRole() {806return AccessibleRole.COMBO_BOX;807}808809/**810* Returns the number of accessible actions available in this object811* If there are more than one, the first one is considered the "default"812* action of the object.813*814* @return the zero-based number of Actions in this object815*/816public int getAccessibleActionCount() {817return 0; // To be fully implemented in a future release818}819820/**821* Returns a description of the specified action of the object.822*823* @param i zero-based index of the actions824* @return a String description of the action825* @see #getAccessibleActionCount826*/827public String getAccessibleActionDescription(int i) {828return null; // To be fully implemented in a future release829}830831/**832* Perform the specified Action on the object833*834* @param i zero-based index of actions835* @return true if the action was performed; otherwise false.836* @see #getAccessibleActionCount837*/838public boolean doAccessibleAction(int i) {839return false; // To be fully implemented in a future release840}841842} // inner class AccessibleAWTChoice843844}845846847