1 /*
   2  * Copyright (c) 2013, 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 import java.awt.*;
  25 import java.awt.event.*;
  26 import javax.swing.*;
  27 import javax.swing.event.*;
  28 
  29 /**
  30  * @test @bug 4245587 4474813 4425878 4767478 8015599
  31  * @key headful
  32  * @author Mark Davidson
  33  * @summary Tests the location of the heavy weight popup portion of JComboBox,
  34  * JMenu and JPopupMenu.
  35  * @library ../regtesthelpers
  36  * @build Util
  37  * @run main TaskbarPositionTest
  38  */
  39 public class TaskbarPositionTest extends JFrame implements ActionListener {
  40 
  41     private boolean done;
  42     private Throwable error;
  43     private static TaskbarPositionTest test;
  44     private static JPopupMenu popupMenu;
  45     private static JPanel panel;
  46     private static JComboBox<String> combo1;
  47     private static JComboBox<String> combo2;
  48     private static JMenuBar menubar;
  49     private static JMenu menu1;
  50     private static JMenu menu2;
  51     private static Rectangle fullScreenBounds;
  52     // The usable desktop space: screen size - screen insets.
  53     private static Rectangle screenBounds;
  54     private static String[] numData = {
  55         "One", "Two", "Three", "Four", "Five", "Six", "Seven"
  56     };
  57     private static String[] dayData = {
  58         "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"
  59     };
  60     private static char[] mnDayData = {
  61         'M', 'T', 'W', 'R', 'F', 'S', 'U'
  62     };
  63 
  64     public TaskbarPositionTest() {
  65         super("Use down to show a JPopupMenu");
  66         setContentPane(panel = createContentPane());
  67         setJMenuBar(createMenuBar("1 - First Menu", true));
  68         setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  69 
  70         //Down will show the popup.
  71         panel.getInputMap().put(KeyStroke.getKeyStroke(
  72                 KeyEvent.VK_DOWN, 0), "OPEN_POPUP");
  73         panel.getActionMap().put("OPEN_POPUP", new PopupHandler());
  74 
  75         pack();
  76 
  77         Toolkit toolkit = Toolkit.getDefaultToolkit();
  78         fullScreenBounds = new Rectangle(new Point(), toolkit.getScreenSize());
  79         screenBounds = new Rectangle(new Point(), toolkit.getScreenSize());
  80 
  81         // Place the frame near the bottom. This is a pretty wild guess.
  82         this.setLocation(0, (int) screenBounds.getHeight() - 2 * this.getHeight());
  83 
  84         // Reduce the screen bounds by the insets.
  85         GraphicsConfiguration gc = this.getGraphicsConfiguration();
  86         if (gc != null) {
  87             Insets screenInsets = toolkit.getScreenInsets(gc);
  88             screenBounds = gc.getBounds();
  89             screenBounds.width -= (screenInsets.left + screenInsets.right);
  90             screenBounds.height -= (screenInsets.top + screenInsets.bottom);
  91             screenBounds.x += screenInsets.left;
  92             screenBounds.y += screenInsets.top;
  93         }
  94 
  95         setVisible(true);
  96     }
  97 
  98     public static class ComboPopupCheckListener implements PopupMenuListener {
  99 
 100         public void popupMenuCanceled(PopupMenuEvent ev) {
 101         }
 102 
 103         public void popupMenuWillBecomeVisible(PopupMenuEvent ev) {
 104         }
 105 
 106         public void popupMenuWillBecomeInvisible(PopupMenuEvent ev) {
 107             Point cpos = combo1.getLocation();
 108             SwingUtilities.convertPointToScreen(cpos, panel);
 109 
 110             JPopupMenu pm = (JPopupMenu) combo1.getUI().getAccessibleChild(combo1, 0);
 111 
 112             if (pm != null) {
 113                 Point p = pm.getLocation();
 114                 SwingUtilities.convertPointToScreen(p, pm);
 115                 if (!UIManager.getLookAndFeel()
 116                         .getName().toLowerCase().contains("os x")) {
 117                     if (p.y < cpos.y) {
 118                          throw new RuntimeException("ComboBox popup is wrongly aligned");
 119                     }  // check that popup was opened down
 120                 }
 121             }
 122         }
 123     }
 124 
 125     private class PopupHandler extends AbstractAction {
 126 
 127         public void actionPerformed(ActionEvent e) {
 128             if (!popupMenu.isVisible()) {
 129                 popupMenu.show((Component) e.getSource(), 40, 40);
 130             }
 131             isPopupOnScreen(popupMenu, fullScreenBounds);
 132         }
 133     }
 134 
 135     class PopupListener extends MouseAdapter {
 136 
 137         private JPopupMenu popup;
 138 
 139         public PopupListener(JPopupMenu popup) {
 140             this.popup = popup;
 141         }
 142 
 143         public void mousePressed(MouseEvent e) {
 144             maybeShowPopup(e);
 145         }
 146 
 147         public void mouseReleased(MouseEvent e) {
 148             maybeShowPopup(e);
 149         }
 150 
 151         private void maybeShowPopup(MouseEvent e) {
 152             if (e.isPopupTrigger()) {
 153                 popup.show(e.getComponent(), e.getX(), e.getY());
 154                 isPopupOnScreen(popup, fullScreenBounds);
 155             }
 156         }
 157     }
 158 
 159     /**
 160      * Tests if the popup is on the screen.
 161      */
 162     public static void isPopupOnScreen(JPopupMenu popup, Rectangle checkBounds) {
 163         Dimension dim = popup.getSize();
 164         Point pt = new Point();
 165         SwingUtilities.convertPointToScreen(pt, popup);
 166         Rectangle bounds = new Rectangle(pt, dim);
 167 
 168         if (!SwingUtilities.isRectangleContainingRectangle(checkBounds, bounds)) {
 169             throw new RuntimeException("We do not match! " + checkBounds + " / " + bounds);
 170         }
 171 
 172     }
 173 
 174     private JPanel createContentPane() {
 175         JPanel panel = new JPanel();
 176 
 177         combo1 = new JComboBox<>(numData);
 178         panel.add(combo1);
 179         combo2 = new JComboBox<>(dayData);
 180         combo2.setEditable(true);
 181         panel.add(combo2);
 182         panel.setSize(300, 200);
 183 
 184         popupMenu = new JPopupMenu();
 185         JMenuItem item;
 186         for (int i = 0; i < dayData.length; i++) {
 187             item = popupMenu.add(new JMenuItem(dayData[i], mnDayData[i]));
 188             item.addActionListener(this);
 189         }
 190         panel.addMouseListener(new PopupListener(popupMenu));
 191 
 192         JTextField field = new JTextField("Press down for Popup");
 193         //Down will show the popup.
 194         field.getInputMap().put(KeyStroke.getKeyStroke(
 195                 KeyEvent.VK_DOWN, 0), "OPEN_POPUP");
 196         field.getActionMap().put("OPEN_POPUP", new PopupHandler());
 197 
 198         panel.add(field);
 199 
 200         return panel;
 201     }
 202 
 203     /**
 204      * @param str name of Menu
 205      * @param bFlag set mnemonics on menu items
 206      */
 207     private JMenuBar createMenuBar(String str, boolean bFlag) {
 208         menubar = new JMenuBar();
 209 
 210         menu1 = new JMenu(str);
 211         menu1.setMnemonic(str.charAt(0));
 212         menu1.addActionListener(this);
 213 
 214         menubar.add(menu1);
 215         for (int i = 0; i < 8; i++) {
 216             JMenuItem menuitem = new JMenuItem("1 JMenuItem" + i);
 217             menuitem.addActionListener(this);
 218             if (bFlag) {
 219                 menuitem.setMnemonic('0' + i);
 220             }
 221             menu1.add(menuitem);
 222         }
 223 
 224         // second menu
 225         menu2 = new JMenu("2 - Second Menu");
 226         menu2.addActionListener(this);
 227         menu2.setMnemonic('2');
 228 
 229         menubar.add(menu2);
 230         for (int i = 0; i < 5; i++) {
 231             JMenuItem menuitem = new JMenuItem("2 JMenuItem" + i);
 232             menuitem.addActionListener(this);
 233 
 234             if (bFlag) {
 235                 menuitem.setMnemonic('0' + i);
 236             }
 237             menu2.add(menuitem);
 238         }
 239         JMenu submenu = new JMenu("Sub Menu");
 240         submenu.setMnemonic('S');
 241         submenu.addActionListener(this);
 242         for (int i = 0; i < 5; i++) {
 243             JMenuItem menuitem = new JMenuItem("S JMenuItem" + i);
 244             menuitem.addActionListener(this);
 245             if (bFlag) {
 246                 menuitem.setMnemonic('0' + i);
 247             }
 248             submenu.add(menuitem);
 249         }
 250         menu2.add(new JSeparator());
 251         menu2.add(submenu);
 252 
 253         return menubar;
 254     }
 255 
 256     public void actionPerformed(ActionEvent evt) {
 257         Object obj = evt.getSource();
 258         if (obj instanceof JMenuItem) {
 259             // put the focus on the noneditable combo.
 260             combo1.requestFocus();
 261         }
 262     }
 263 
 264     public static void main(String[] args) throws Throwable {
 265 
 266         SwingUtilities.invokeAndWait(new Runnable() {
 267             public void run() {
 268                 test = new TaskbarPositionTest();
 269             }
 270         });
 271 
 272         // Use Robot to automate the test
 273         Robot robot;
 274         robot = new Robot();
 275         robot.setAutoDelay(125);
 276 
 277         // 1 - menu
 278         Util.hitMnemonics(robot, KeyEvent.VK_1);
 279 
 280         robot.waitForIdle();
 281         isPopupOnScreen(menu1.getPopupMenu(), screenBounds);
 282 
 283         // 2 menu with sub menu
 284         robot.keyPress(KeyEvent.VK_RIGHT);
 285         robot.keyRelease(KeyEvent.VK_RIGHT);
 286         Util.hitMnemonics(robot, KeyEvent.VK_S);
 287 
 288         robot.waitForIdle();
 289         isPopupOnScreen(menu2.getPopupMenu(), screenBounds);
 290 
 291         robot.keyPress(KeyEvent.VK_ENTER);
 292         robot.keyRelease(KeyEvent.VK_ENTER);
 293 
 294         // Focus should go to non editable combo box
 295         robot.waitForIdle();
 296         Thread.sleep(500);
 297 
 298         robot.keyPress(KeyEvent.VK_DOWN);
 299 
 300         // How do we check combo boxes?
 301         // Editable combo box
 302         robot.keyPress(KeyEvent.VK_TAB);
 303         robot.keyRelease(KeyEvent.VK_TAB);
 304         robot.keyPress(KeyEvent.VK_DOWN);
 305         robot.keyRelease(KeyEvent.VK_DOWN);
 306 
 307         // combo1.getUI();
 308         // Popup from Text field
 309         robot.keyPress(KeyEvent.VK_TAB);
 310         robot.keyRelease(KeyEvent.VK_TAB);
 311         robot.keyPress(KeyEvent.VK_DOWN);
 312         robot.keyRelease(KeyEvent.VK_DOWN);
 313 
 314         // Popup from a mouse click.
 315         Point pt = new Point(2, 2);
 316         SwingUtilities.convertPointToScreen(pt, panel);
 317         robot.mouseMove((int) pt.getX(), (int) pt.getY());
 318         robot.mousePress(InputEvent.BUTTON3_MASK);
 319         robot.mouseRelease(InputEvent.BUTTON3_MASK);
 320 
 321         robot.waitForIdle();
 322         SwingUtilities.invokeAndWait(new Runnable() {
 323             public void run() {
 324                 test.setLocation(-30, 100);
 325                 combo1.addPopupMenuListener(new ComboPopupCheckListener());
 326                 combo1.requestFocus();
 327             }
 328         });
 329 
 330         robot.keyPress(KeyEvent.VK_DOWN);
 331         robot.keyRelease(KeyEvent.VK_DOWN);
 332         robot.keyPress(KeyEvent.VK_ESCAPE);
 333         robot.keyRelease(KeyEvent.VK_ESCAPE);
 334 
 335         robot.waitForIdle();
 336         Thread.sleep(500);
 337         disposeGUI();
 338     }
 339 
 340     private static void disposeGUI() throws Exception {
 341         SwingUtilities.invokeAndWait(new Runnable() {
 342             public void run() {
 343                 test.dispose();
 344             }
 345         });
 346     }
 347 }
 348