blob: 27d43b1bfce6973565b76f8496778014286b023a [file] [log] [blame]
/*
* 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;
}
}
}