jdk/src/java.desktop/macosx/classes/sun/lwawt/LWChoicePeer.java
changeset 25859 3317bb8137f4
parent 24565 b63e70ae575d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/macosx/classes/sun/lwawt/LWChoicePeer.java	Sun Aug 17 15:54:13 2014 +0100
@@ -0,0 +1,197 @@
+/*
+ * 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;
+        }
+    }
+}