/* * Copyright (c) 2011, 2013, 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 sun.lwawt; import java.awt.*; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.peer.ChoicePeer; import javax.accessibility.Accessible; import javax.swing.*; /** * Lightweight implementation of {@link ChoicePeer}. Delegates most of the work * to the {@link JComboBox}. */ final class LWChoicePeer extends LWComponentPeer<Choice, JComboBox<String>> implements ChoicePeer, ItemListener { /** * According to Choice specification item events are sent in response to * user input, but not in response to calls to select(). But JComboBox are * sent item events in both cases. Should be used under delegateLock. */ private boolean skipPostMessage; LWChoicePeer(final Choice target, final PlatformComponent platformComponent) { super(target, platformComponent); } @Override JComboBox<String> createDelegate() { return new JComboBoxDelegate(); } @Override void initializeImpl() { super.initializeImpl(); final Choice choice = getTarget(); final JComboBox<String> combo = getDelegate(); synchronized (getDelegateLock()) { final int count = choice.getItemCount(); for (int i = 0; i < count; ++i) { combo.addItem(choice.getItem(i)); } select(choice.getSelectedIndex()); // NOTE: the listener must be added at the very end, otherwise it // fires events upon initialization of the combo box. combo.addItemListener(this); } } @Override public void itemStateChanged(final ItemEvent e) { // AWT Choice sends SELECTED event only whereas JComboBox // sends both SELECTED and DESELECTED. if (e.getStateChange() == ItemEvent.SELECTED) { synchronized (getDelegateLock()) { if (skipPostMessage) { return; } getTarget().select(getDelegate().getSelectedIndex()); } postEvent(new ItemEvent(getTarget(), ItemEvent.ITEM_STATE_CHANGED, e.getItem(), ItemEvent.SELECTED)); } } @Override public void add(final String item, final int index) { synchronized (getDelegateLock()) { getDelegate().insertItemAt(item, index); } } @Override public void remove(final int index) { synchronized (getDelegateLock()) { // We shouldn't post event, if selected item was removed. skipPostMessage = true; getDelegate().removeItemAt(index); skipPostMessage = false; } } @Override public void removeAll() { synchronized (getDelegateLock()) { getDelegate().removeAllItems(); } } @Override public void select(final int index) { synchronized (getDelegateLock()) { if (index != getDelegate().getSelectedIndex()) { skipPostMessage = true; getDelegate().setSelectedIndex(index); skipPostMessage = false; } } } @Override public boolean isFocusable() { return true; } @SuppressWarnings("serial")// Safe: outer class is non-serializable. private final class JComboBoxDelegate extends JComboBox<String> { // Empty non private constructor was added because access to this // class shouldn't be emulated by a synthetic accessor method. JComboBoxDelegate() { super(); } @Override public boolean hasFocus() { return getTarget().hasFocus(); } //Needed for proper popup menu location @Override public Point getLocationOnScreen() { return LWChoicePeer.this.getLocationOnScreen(); } /** * We should post ITEM_STATE_CHANGED event when the same element is * reselected. */ @Override public void setSelectedItem(final Object anObject) { final Object oldSelection = selectedItemReminder; if (oldSelection != null && oldSelection.equals(anObject)) { selectedItemChanged(); } super.setSelectedItem(anObject); } @Override public void firePopupMenuWillBecomeVisible() { super.firePopupMenuWillBecomeVisible(); SwingUtilities.invokeLater(() -> { JPopupMenu popupMenu = getPopupMenu(); // Need to override the invoker for proper grab handling if (popupMenu != null && popupMenu.isShowing() && popupMenu.getInvoker() != getTarget()) { // The popup is now visible with correct location // Save it and restore after toggling visibility and changing invoker Point loc = popupMenu.getLocationOnScreen(); SwingUtilities.convertPointFromScreen(loc, this); popupMenu.setVisible(false); popupMenu.show(getTarget(), loc.x, loc.y); } }); } private JPopupMenu getPopupMenu() { for (int i = 0; i < getAccessibleContext().getAccessibleChildrenCount(); i++) { Accessible child = getAccessibleContext().getAccessibleChild(i); if (child instanceof JPopupMenu) { return (JPopupMenu) child; } } return null; } } }