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
24import java.awt.*;
25import java.awt.event.*;
26import javax.swing.*;
27import 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 */
39public 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 CTRL-down to show a JPopupMenu");
66        setContentPane(panel = createContentPane());
67        setJMenuBar(createMenuBar("1 - First Menu", true));
68        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
69
70        // CTRL-down will show the popup.
71        panel.getInputMap().put(KeyStroke.getKeyStroke(
72                KeyEvent.VK_DOWN, InputEvent.CTRL_MASK), "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 (p.y < cpos.y) {
116                    throw new RuntimeException("ComboBox popup is wrongly aligned");
117                }  // check that popup was opened down
118            }
119        }
120    }
121
122    private class PopupHandler extends AbstractAction {
123
124        public void actionPerformed(ActionEvent e) {
125            if (!popupMenu.isVisible()) {
126                popupMenu.show((Component) e.getSource(), 40, 40);
127            }
128            isPopupOnScreen(popupMenu, fullScreenBounds);
129        }
130    }
131
132    class PopupListener extends MouseAdapter {
133
134        private JPopupMenu popup;
135
136        public PopupListener(JPopupMenu popup) {
137            this.popup = popup;
138        }
139
140        public void mousePressed(MouseEvent e) {
141            maybeShowPopup(e);
142        }
143
144        public void mouseReleased(MouseEvent e) {
145            maybeShowPopup(e);
146        }
147
148        private void maybeShowPopup(MouseEvent e) {
149            if (e.isPopupTrigger()) {
150                popup.show(e.getComponent(), e.getX(), e.getY());
151                isPopupOnScreen(popup, fullScreenBounds);
152            }
153        }
154    }
155
156    /**
157     * Tests if the popup is on the screen.
158     */
159    public static void isPopupOnScreen(JPopupMenu popup, Rectangle checkBounds) {
160        Dimension dim = popup.getSize();
161        Point pt = new Point();
162        SwingUtilities.convertPointToScreen(pt, popup);
163        Rectangle bounds = new Rectangle(pt, dim);
164
165        if (!SwingUtilities.isRectangleContainingRectangle(checkBounds, bounds)) {
166            throw new RuntimeException("We do not match! " + checkBounds + " / " + bounds);
167        }
168
169    }
170
171    private JPanel createContentPane() {
172        JPanel panel = new JPanel();
173
174        combo1 = new JComboBox<>(numData);
175        panel.add(combo1);
176        combo2 = new JComboBox<>(dayData);
177        combo2.setEditable(true);
178        panel.add(combo2);
179        panel.setSize(300, 200);
180
181        popupMenu = new JPopupMenu();
182        JMenuItem item;
183        for (int i = 0; i < dayData.length; i++) {
184            item = popupMenu.add(new JMenuItem(dayData[i], mnDayData[i]));
185            item.addActionListener(this);
186        }
187        panel.addMouseListener(new PopupListener(popupMenu));
188
189        JTextField field = new JTextField("CTRL+down for Popup");
190        // CTRL-down will show the popup.
191        field.getInputMap().put(KeyStroke.getKeyStroke(
192                KeyEvent.VK_DOWN, InputEvent.CTRL_MASK), "OPEN_POPUP");
193        field.getActionMap().put("OPEN_POPUP", new PopupHandler());
194
195        panel.add(field);
196
197        return panel;
198    }
199
200    /**
201     * @param str name of Menu
202     * @param bFlag set mnemonics on menu items
203     */
204    private JMenuBar createMenuBar(String str, boolean bFlag) {
205        menubar = new JMenuBar();
206
207        menu1 = new JMenu(str);
208        menu1.setMnemonic(str.charAt(0));
209        menu1.addActionListener(this);
210
211        menubar.add(menu1);
212        for (int i = 0; i < 8; i++) {
213            JMenuItem menuitem = new JMenuItem("1 JMenuItem" + i);
214            menuitem.addActionListener(this);
215            if (bFlag) {
216                menuitem.setMnemonic('0' + i);
217            }
218            menu1.add(menuitem);
219        }
220
221        // second menu
222        menu2 = new JMenu("2 - Second Menu");
223        menu2.addActionListener(this);
224        menu2.setMnemonic('2');
225
226        menubar.add(menu2);
227        for (int i = 0; i < 5; i++) {
228            JMenuItem menuitem = new JMenuItem("2 JMenuItem" + i);
229            menuitem.addActionListener(this);
230
231            if (bFlag) {
232                menuitem.setMnemonic('0' + i);
233            }
234            menu2.add(menuitem);
235        }
236        JMenu submenu = new JMenu("Sub Menu");
237        submenu.setMnemonic('S');
238        submenu.addActionListener(this);
239        for (int i = 0; i < 5; i++) {
240            JMenuItem menuitem = new JMenuItem("S JMenuItem" + i);
241            menuitem.addActionListener(this);
242            if (bFlag) {
243                menuitem.setMnemonic('0' + i);
244            }
245            submenu.add(menuitem);
246        }
247        menu2.add(new JSeparator());
248        menu2.add(submenu);
249
250        return menubar;
251    }
252
253    public void actionPerformed(ActionEvent evt) {
254        Object obj = evt.getSource();
255        if (obj instanceof JMenuItem) {
256            // put the focus on the noneditable combo.
257            combo1.requestFocus();
258        }
259    }
260
261    public static void main(String[] args) throws Throwable {
262
263
264        SwingUtilities.invokeAndWait(new Runnable() {
265            public void run() {
266                test = new TaskbarPositionTest();
267            }
268        });
269
270        // Use Robot to automate the test
271        Robot robot;
272        robot = new Robot();
273        robot.setAutoDelay(125);
274
275        // 1 - menu
276        Util.hitMnemonics(robot, KeyEvent.VK_1);
277
278        robot.waitForIdle();
279        isPopupOnScreen(menu1.getPopupMenu(), screenBounds);
280
281        // 2 menu with sub menu
282        robot.keyPress(KeyEvent.VK_RIGHT);
283        robot.keyRelease(KeyEvent.VK_RIGHT);
284        Util.hitMnemonics(robot, KeyEvent.VK_S);
285
286        robot.waitForIdle();
287        isPopupOnScreen(menu2.getPopupMenu(), screenBounds);
288
289        robot.keyPress(KeyEvent.VK_ENTER);
290        robot.keyRelease(KeyEvent.VK_ENTER);
291
292        // Focus should go to non editable combo box
293        robot.waitForIdle();
294        Thread.sleep(500);
295
296        robot.keyPress(KeyEvent.VK_DOWN);
297
298        // How do we check combo boxes?
299
300        // Editable combo box
301        robot.keyPress(KeyEvent.VK_TAB);
302        robot.keyRelease(KeyEvent.VK_TAB);
303        robot.keyPress(KeyEvent.VK_DOWN);
304        robot.keyRelease(KeyEvent.VK_DOWN);
305
306        // combo1.getUI();
307
308        // Popup from Text field
309        robot.keyPress(KeyEvent.VK_TAB);
310        robot.keyRelease(KeyEvent.VK_TAB);
311        robot.keyPress(KeyEvent.VK_CONTROL);
312        robot.keyPress(KeyEvent.VK_DOWN);
313        robot.keyRelease(KeyEvent.VK_DOWN);
314        robot.keyRelease(KeyEvent.VK_CONTROL);
315
316        // Popup from a mouse click.
317        Point pt = new Point(2, 2);
318        SwingUtilities.convertPointToScreen(pt, panel);
319        robot.mouseMove((int) pt.getX(), (int) pt.getY());
320        robot.mousePress(InputEvent.BUTTON3_MASK);
321        robot.mouseRelease(InputEvent.BUTTON3_MASK);
322
323        robot.waitForIdle();
324        SwingUtilities.invokeAndWait(new Runnable() {
325            public void run() {
326                test.setLocation(-30, 100);
327                combo1.addPopupMenuListener(new ComboPopupCheckListener());
328                combo1.requestFocus();
329            }
330        });
331
332        robot.keyPress(KeyEvent.VK_DOWN);
333        robot.keyRelease(KeyEvent.VK_DOWN);
334        robot.keyPress(KeyEvent.VK_ESCAPE);
335        robot.keyRelease(KeyEvent.VK_ESCAPE);
336
337        robot.waitForIdle();
338        Thread.sleep(500);
339    }
340}
341