1 /*
   2  * Copyright (c) 2015, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @key headful
  27  * @bug 8072900
  28  * @summary Mouse events are captured by the wrong menu in OS X
  29  * @author Anton Nashatyrev
  30  * @run main WrongSelectionOnMouseOver
  31  */
  32 
  33 import javax.swing.*;
  34 import javax.swing.event.MenuEvent;
  35 import javax.swing.event.MenuListener;
  36 import java.awt.*;
  37 import java.awt.event.MouseAdapter;
  38 import java.awt.event.MouseEvent;
  39 import java.util.concurrent.CountDownLatch;
  40 import java.util.concurrent.TimeUnit;
  41 
  42 import static javax.swing.UIManager.getInstalledLookAndFeels;
  43 
  44 public class WrongSelectionOnMouseOver implements Runnable {
  45 
  46     CountDownLatch firstMenuSelected = new CountDownLatch(1);
  47     CountDownLatch secondMenuMouseEntered = new CountDownLatch(1);
  48     CountDownLatch secondMenuSelected = new CountDownLatch(1);
  49 
  50     JMenu m1, m2;
  51 
  52     private UIManager.LookAndFeelInfo laf;
  53     JFrame frame1;
  54     JFrame frame2;
  55 
  56     public WrongSelectionOnMouseOver(UIManager.LookAndFeelInfo laf) throws Exception {
  57         this.laf = laf;
  58     }
  59 
  60     private void createUI() throws Exception {
  61         System.out.println("Testing UI: " + laf);
  62         UIManager.setLookAndFeel(laf.getClassName());
  63 
  64         {
  65             frame1 = new JFrame("Frame1");
  66             JMenuBar mb = new JMenuBar();
  67             m1 = new JMenu("File");
  68             JMenuItem i1 = new JMenuItem("Save");
  69             JMenuItem i2 = new JMenuItem("Load");
  70 
  71             m1.addMenuListener(new MenuListener() {
  72                 @Override
  73                 public void menuSelected(MenuEvent e) {
  74                     firstMenuSelected.countDown();
  75                     System.out.println("Menu1: menuSelected");
  76                 }
  77 
  78                 @Override
  79                 public void menuDeselected(MenuEvent e) {
  80                     System.out.println("Menu1: menuDeselected");
  81                 }
  82 
  83                 @Override
  84                 public void menuCanceled(MenuEvent e) {
  85                     System.out.println("Menu1: menuCanceled");
  86                 }
  87             });
  88 
  89             frame1.setJMenuBar(mb);
  90             mb.add(m1);
  91             m1.add(i1);
  92             m1.add(i2);
  93 
  94             frame1.setLayout(new FlowLayout());
  95             frame1.setBounds(200, 200, 200, 200);
  96 
  97             frame1.setVisible(true);
  98         }
  99 
 100         {
 101             frame2 = new JFrame("Frame2");
 102             JMenuBar mb = new JMenuBar();
 103             m2 = new JMenu("File");
 104             JMenuItem i1 = new JMenuItem("Save");
 105             JMenuItem i2 = new JMenuItem("Load");
 106 
 107             m2.addMouseListener(new MouseAdapter() {
 108                 @Override
 109                 public void mouseEntered(MouseEvent e) {
 110                     secondMenuMouseEntered.countDown();
 111                     System.out.println("WrongSelectionOnMouseOver.mouseEntered");
 112                 }
 113             });
 114 
 115             m2.addMenuListener(new MenuListener() {
 116                 @Override
 117                 public void menuSelected(MenuEvent e) {
 118                     secondMenuSelected.countDown();
 119                     System.out.println("Menu2: menuSelected");
 120                 }
 121 
 122                 @Override
 123                 public void menuDeselected(MenuEvent e) {
 124                     System.out.println("Menu2: menuDeselected");
 125                 }
 126 
 127                 @Override
 128                 public void menuCanceled(MenuEvent e) {
 129                     System.out.println("Menu2: menuCanceled");
 130                 }
 131             });
 132 
 133             frame2.setJMenuBar(mb);
 134             mb.add(m2);
 135             m2.add(i1);
 136             m2.add(i2);
 137 
 138             frame2.setLayout(new FlowLayout());
 139             frame2.setBounds(400, 200, 200, 200);
 140 
 141             frame2.setVisible(true);
 142         }
 143     }
 144 
 145     public void disposeUI() {
 146         frame1.dispose();
 147         frame2.dispose();
 148     }
 149 
 150     @Override
 151     public void run() {
 152         try {
 153             if (frame1 == null) {
 154                 createUI();
 155             } else {
 156                 disposeUI();
 157             }
 158         } catch (Exception e) {
 159             throw new RuntimeException(e);
 160         }
 161     }
 162 
 163     public void test() throws Exception {
 164         Robot robot = new Robot();
 165         robot.setAutoDelay(100);
 166 
 167         robot.waitForIdle();
 168 
 169         robot.mouseMove((int) m1.getLocationOnScreen().getX() + 5,
 170                 (int) m1.getLocationOnScreen().getY() + 5);
 171         robot.mousePress(MouseEvent.BUTTON1_MASK);
 172         robot.mouseRelease(MouseEvent.BUTTON1_MASK);
 173 
 174         if (!firstMenuSelected.await(5, TimeUnit.SECONDS)) {
 175             throw new RuntimeException("Menu has not been selected.");
 176         };
 177 
 178         robot.mouseMove((int) m2.getLocationOnScreen().getX() + 5,
 179                 (int) m2.getLocationOnScreen().getY() + 5);
 180 
 181         if (!secondMenuMouseEntered.await(5, TimeUnit.SECONDS)) {
 182             throw new RuntimeException("MouseEntered event missed for the second menu");
 183         };
 184 
 185         if (secondMenuSelected.await(1, TimeUnit.SECONDS)) {
 186             throw new RuntimeException("The second menu has been selected");
 187         };
 188     }
 189 
 190     public static void main(final String[] args) throws Exception {
 191         for (final UIManager.LookAndFeelInfo laf : getInstalledLookAndFeels()) {
 192             WrongSelectionOnMouseOver test = new WrongSelectionOnMouseOver(laf);
 193             SwingUtilities.invokeAndWait(test);
 194             test.test();
 195             SwingUtilities.invokeAndWait(test);
 196         }
 197         System.out.println("Test passed");
 198     }
 199 }