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 @bug 8072900
  27 @summary Mouse events are captured by the wrong menu in OS X
  28 @author Anton Nashatyrev
  29 @run main WrongSelectionOnMouseOver
  30 */
  31 
  32 import javax.swing.*;
  33 import javax.swing.event.MenuEvent;
  34 import javax.swing.event.MenuListener;
  35 import java.awt.*;
  36 import java.awt.event.MouseAdapter;
  37 import java.awt.event.MouseEvent;
  38 import java.util.concurrent.CountDownLatch;
  39 import java.util.concurrent.TimeUnit;
  40 
  41 import static javax.swing.UIManager.getInstalledLookAndFeels;
  42 
  43 public class WrongSelectionOnMouseOver implements Runnable {
  44 
  45     CountDownLatch firstMenuSelected = new CountDownLatch(1);
  46     CountDownLatch secondMenuMouseEntered = new CountDownLatch(1);
  47     CountDownLatch secondMenuSelected = new CountDownLatch(1);
  48 
  49     JMenu m1, m2;
  50 
  51     private UIManager.LookAndFeelInfo laf;
  52     JFrame frame1;
  53     JFrame frame2;
  54 
  55     public WrongSelectionOnMouseOver(UIManager.LookAndFeelInfo laf) throws Exception {
  56         this.laf = laf;
  57     }
  58 
  59     private void createUI() throws Exception {
  60         System.out.println("Testing UI: " + laf);
  61         UIManager.setLookAndFeel(laf.getClassName());
  62 
  63         {
  64             frame1 = new JFrame("Frame1");
  65             JMenuBar mb = new JMenuBar();
  66             m1 = new JMenu("File");
  67             JMenuItem i1 = new JMenuItem("Save");
  68             JMenuItem i2 = new JMenuItem("Load");
  69 
  70             m1.addMenuListener(new MenuListener() {
  71                 @Override
  72                 public void menuSelected(MenuEvent e) {
  73                     firstMenuSelected.countDown();
  74                     System.out.println("Menu1: menuSelected");
  75                 }
  76 
  77                 @Override
  78                 public void menuDeselected(MenuEvent e) {
  79                     System.out.println("Menu1: menuDeselected");
  80                 }
  81 
  82                 @Override
  83                 public void menuCanceled(MenuEvent e) {
  84                     System.out.println("Menu1: menuCanceled");
  85                 }
  86             });
  87 
  88             frame1.setJMenuBar(mb);
  89             mb.add(m1);
  90             m1.add(i1);
  91             m1.add(i2);
  92 
  93             frame1.setLayout(new FlowLayout());
  94             frame1.setBounds(200, 200, 200, 200);
  95 
  96             frame1.setVisible(true);
  97         }
  98 
  99         {
 100             frame2 = new JFrame("Frame2");
 101             JMenuBar mb = new JMenuBar();
 102             m2 = new JMenu("File");
 103             JMenuItem i1 = new JMenuItem("Save");
 104             JMenuItem i2 = new JMenuItem("Load");
 105 
 106             m2.addMouseListener(new MouseAdapter() {
 107                 @Override
 108                 public void mouseEntered(MouseEvent e) {
 109                     secondMenuMouseEntered.countDown();
 110                     System.out.println("WrongSelectionOnMouseOver.mouseEntered");
 111                 }
 112             });
 113 
 114             m2.addMenuListener(new MenuListener() {
 115                 @Override
 116                 public void menuSelected(MenuEvent e) {
 117                     secondMenuSelected.countDown();
 118                     System.out.println("Menu2: menuSelected");
 119                 }
 120 
 121                 @Override
 122                 public void menuDeselected(MenuEvent e) {
 123                     System.out.println("Menu2: menuDeselected");
 124                 }
 125 
 126                 @Override
 127                 public void menuCanceled(MenuEvent e) {
 128                     System.out.println("Menu2: menuCanceled");
 129                 }
 130             });
 131 
 132             frame2.setJMenuBar(mb);
 133             mb.add(m2);
 134             m2.add(i1);
 135             m2.add(i2);
 136 
 137             frame2.setLayout(new FlowLayout());
 138             frame2.setBounds(400, 200, 200, 200);
 139 
 140             frame2.setVisible(true);
 141         }
 142     }
 143 
 144     public void disposeUI() {
 145         frame1.dispose();
 146         frame2.dispose();
 147     }
 148 
 149     @Override
 150     public void run() {
 151         try {
 152             if (frame1 == null) {
 153                 createUI();
 154             } else {
 155                 disposeUI();
 156             }
 157         } catch (Exception e) {
 158             throw new RuntimeException(e);
 159         }
 160     }
 161 
 162     public void test() throws Exception {
 163         Robot robot = new Robot();
 164         robot.setAutoDelay(100);
 165 
 166         robot.waitForIdle();
 167 
 168         robot.mouseMove((int) m1.getLocationOnScreen().getX() + 5,
 169                 (int) m1.getLocationOnScreen().getY() + 5);
 170         robot.mousePress(MouseEvent.BUTTON1_MASK);
 171         robot.mouseRelease(MouseEvent.BUTTON1_MASK);
 172 
 173         if (!firstMenuSelected.await(5, TimeUnit.SECONDS)) {
 174             throw new RuntimeException("Menu has not been selected.");
 175         };
 176 
 177         robot.mouseMove((int) m2.getLocationOnScreen().getX() + 5,
 178                 (int) m2.getLocationOnScreen().getY() + 5);
 179 
 180         if (!secondMenuMouseEntered.await(5, TimeUnit.SECONDS)) {
 181             throw new RuntimeException("MouseEntered event missed for the second menu");
 182         };
 183 
 184         if (secondMenuSelected.await(1, TimeUnit.SECONDS)) {
 185             throw new RuntimeException("The second menu has been selected");
 186         };
 187     }
 188 
 189     public static void main(final String[] args) throws Exception {
 190         for (final UIManager.LookAndFeelInfo laf : getInstalledLookAndFeels()) {
 191             WrongSelectionOnMouseOver test = new WrongSelectionOnMouseOver(laf);
 192             SwingUtilities.invokeAndWait(test);
 193             test.test();
 194             SwingUtilities.invokeAndWait(test);
 195         }
 196         System.out.println("Test passed");
 197     }
 198 }