1/*
2 * Copyright (c) 2002, 2017, 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.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25package sun.awt.X11;
26
27import java.awt.*;
28import java.awt.peer.*;
29import java.awt.event.*;
30
31import java.util.Vector;
32import sun.awt.AWTAccessor;
33import sun.util.logging.PlatformLogger;
34
35public class XPopupMenuPeer extends XMenuWindow implements PopupMenuPeer {
36
37    /************************************************
38     *
39     * Data members
40     *
41     ************************************************/
42    private static PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XBaseMenuWindow");
43
44    /*
45     * Primary members
46     */
47    private XComponentPeer componentPeer;
48    private PopupMenu popupMenuTarget;
49
50    /*
51     * If mouse button is clicked on item showing submenu
52     * we have to hide its submenu.
53     * This member saves the submenu under cursor
54     * Only if it's showing
55     */
56    private XMenuPeer showingMousePressedSubmenu = null;
57
58    /*
59     * Painting constants
60     */
61    private static final int CAPTION_MARGIN_TOP = 4;
62    private static final int CAPTION_SEPARATOR_HEIGHT = 6;
63
64    /************************************************
65     *
66     * Construction
67     *
68     ************************************************/
69    XPopupMenuPeer(PopupMenu target) {
70        super(null);
71        this.popupMenuTarget = target;
72    }
73
74    /************************************************
75     *
76     * Implementation of interface methods
77     *
78     ************************************************/
79    /*
80     * From MenuComponentPeer
81     */
82    public void setFont(Font f) {
83        resetMapping();
84        setItemsFont(f);
85        postPaintEvent();
86    }
87
88    /*
89     * From MenuItemPeer
90     */
91    public void setLabel(String label) {
92        resetMapping();
93        postPaintEvent();
94    }
95
96
97    public void setEnabled(boolean enabled) {
98        postPaintEvent();
99    }
100
101    /**
102     * DEPRECATED:  Replaced by setEnabled(boolean).
103     * @see java.awt.peer.MenuItemPeer
104     */
105    public void enable() {
106        setEnabled( true );
107    }
108
109    /**
110     * DEPRECATED:  Replaced by setEnabled(boolean).
111     * @see java.awt.peer.MenuItemPeer
112     */
113    public void disable() {
114        setEnabled( false );
115    }
116
117    /*
118     * From MenuPeer
119     */
120    /**
121     * addSeparator routines are not used
122     * in peers. Shared code invokes addItem("-")
123     * for adding separators
124     */
125    public void addSeparator() {
126        if (log.isLoggable(PlatformLogger.Level.FINER)) {
127            log.finer("addSeparator is not implemented");
128        }
129    }
130
131    /*
132     * From PopupMenuPeer
133     */
134    @SuppressWarnings("deprecation")
135    public void show(Event e) {
136        target = (Component)e.target;
137        // Get menus from the target.
138        Vector<MenuItem> targetItemVector = getMenuTargetItems();
139        if (targetItemVector != null) {
140            reloadItems(targetItemVector);
141            //Fix for 6287092: JCK15a: api/java_awt/interactive/event/EventTests.html#EventTest0015 fails, mustang
142            Point tl = target.getLocationOnScreen();
143            Point pt = new Point(tl.x + e.x, tl.y + e.y);
144            //Fixed 6266513: Incorrect key handling in XAWT popup menu
145            //No item should be selected when showing popup menu
146            if (!ensureCreated()) {
147                return;
148            }
149            Dimension dim = getDesiredSize();
150            //Fix for 6267162: PIT: Popup Menu gets hidden below the screen when opened
151            //near the periphery of the screen, XToolkit
152            Rectangle bounds = getWindowBounds(pt, dim);
153            reshape(bounds);
154            xSetVisible(true);
155            toFront();
156            selectItem(null, false);
157            grabInput();
158        }
159    }
160
161    /************************************************
162     *
163     * Access to target's fields
164     *
165     ************************************************/
166
167    //Fix for 6267144: PIT: Popup menu label is not shown, XToolkit
168    Font getTargetFont() {
169        if (popupMenuTarget == null) {
170            return XWindow.getDefaultFont();
171        }
172        return AWTAccessor.getMenuComponentAccessor()
173                   .getFont_NoClientCode(popupMenuTarget);
174    }
175
176    //Fix for 6267144: PIT: Popup menu label is not shown, XToolkit
177    String getTargetLabel() {
178        if (target == null) {
179            return "";
180        }
181        return AWTAccessor.getMenuItemAccessor().getLabel(popupMenuTarget);
182    }
183
184    //Fix for 6184485: Popup menu is not disabled on XToolkit even when calling setEnabled (false)
185    boolean isTargetEnabled() {
186        if (popupMenuTarget == null) {
187            return false;
188        }
189        return AWTAccessor.getMenuItemAccessor().isEnabled(popupMenuTarget);
190    }
191
192    Vector<MenuItem> getMenuTargetItems() {
193        if (popupMenuTarget == null) {
194            return null;
195        }
196        return AWTAccessor.getMenuAccessor().getItems(popupMenuTarget);
197    }
198
199    /************************************************
200     *
201     * Utility functions
202     *
203     ************************************************/
204
205    //Fix for 6267162: PIT: Popup Menu gets hidden below the screen when opened
206    //near the periphery of the screen, XToolkit
207
208    /**
209     * Calculates placement of popup menu window
210     * given origin in global coordinates and
211     * size of menu window. Returns suggested
212     * rectangle for menu window in global coordinates
213     * @param origin the origin point specified in show()
214     * function converted to global coordinates
215     * @param windowSize the desired size of menu's window
216     */
217    protected Rectangle getWindowBounds(Point origin, Dimension windowSize) {
218        Rectangle globalBounds = new Rectangle(origin.x, origin.y, 0, 0);
219        Rectangle screenBounds = getCurrentGraphicsConfiguration().getBounds();
220        Rectangle res;
221        res = fitWindowRight(globalBounds, windowSize, screenBounds);
222        if (res != null) {
223            return res;
224        }
225        res = fitWindowLeft(globalBounds, windowSize, screenBounds);
226        if (res != null) {
227            return res;
228        }
229        res = fitWindowBelow(globalBounds, windowSize, screenBounds);
230        if (res != null) {
231            return res;
232        }
233        res = fitWindowAbove(globalBounds, windowSize, screenBounds);
234        if (res != null) {
235            return res;
236        }
237        return fitWindowToScreen(windowSize, screenBounds);
238   }
239
240    /************************************************
241     *
242     * Overriden XMenuWindow caption-painting functions
243     * Necessary to fix 6267144: PIT: Popup menu label is not shown, XToolkit
244     *
245     ************************************************/
246    /**
247     * Returns height of menu window's caption.
248     * Can be overriden for popup menus and tear-off menus
249     */
250    protected Dimension getCaptionSize() {
251        String s = getTargetLabel();
252        if (s.equals("")) {
253            return null;
254        }
255        Graphics g = getGraphics();
256        if (g == null) {
257            return null;
258        }
259        try {
260            g.setFont(getTargetFont());
261            FontMetrics fm = g.getFontMetrics();
262            String str = getTargetLabel();
263            int width = fm.stringWidth(str);
264            int height = CAPTION_MARGIN_TOP + fm.getHeight() + CAPTION_SEPARATOR_HEIGHT;
265            Dimension textDimension = new Dimension(width, height);
266            return textDimension;
267        } finally {
268            g.dispose();
269        }
270    }
271
272    /**
273     * Paints menu window's caption.
274     * Can be overriden for popup menus and tear-off menus.
275     * Default implementation does nothing
276     */
277    protected void paintCaption(Graphics g, Rectangle rect) {
278        String s = getTargetLabel();
279        if (s.equals("")) {
280            return;
281        }
282        g.setFont(getTargetFont());
283        FontMetrics fm = g.getFontMetrics();
284        String str = getTargetLabel();
285        int width = fm.stringWidth(str);
286        int textx = rect.x + (rect.width - width) / 2;
287        int texty = rect.y + CAPTION_MARGIN_TOP + fm.getAscent();
288        int sepy = rect.y + rect.height - CAPTION_SEPARATOR_HEIGHT / 2;
289        g.setColor(isTargetEnabled() ? getForegroundColor() : getDisabledColor());
290        g.drawString(s, textx, texty);
291        draw3DRect(g, rect.x, sepy,  rect.width, 2, false);
292    }
293
294    /************************************************
295     *
296     * Overriden XBaseMenuWindow functions
297     *
298     ************************************************/
299    protected void doDispose() {
300        super.doDispose();
301        XToolkit.targetDisposedPeer(popupMenuTarget, this);
302    }
303
304    protected void handleEvent(AWTEvent event) {
305        switch(event.getID()) {
306        case MouseEvent.MOUSE_PRESSED:
307        case MouseEvent.MOUSE_RELEASED:
308        case MouseEvent.MOUSE_CLICKED:
309        case MouseEvent.MOUSE_MOVED:
310        case MouseEvent.MOUSE_ENTERED:
311        case MouseEvent.MOUSE_EXITED:
312        case MouseEvent.MOUSE_DRAGGED:
313            doHandleJavaMouseEvent((MouseEvent)event);
314            break;
315        case KeyEvent.KEY_PRESSED:
316        case KeyEvent.KEY_RELEASED:
317            doHandleJavaKeyEvent((KeyEvent)event);
318            break;
319        default:
320            super.handleEvent(event);
321            break;
322        }
323    }
324
325    /************************************************
326     *
327     * Overriden XWindow general-purpose functions
328     *
329     ************************************************/
330    void ungrabInputImpl() {
331        hide();
332    }
333
334    /************************************************
335     *
336     * Overriden XWindow keyboard processing
337     *
338     ************************************************/
339
340    /*
341     * In previous version keys were handled in handleKeyPress.
342     * Now we override this function do disable F10 explicit
343     * processing. All processing is done using KeyEvent.
344     */
345    public void handleKeyPress(XEvent xev) {
346        XKeyEvent xkey = xev.get_xkey();
347        if (log.isLoggable(PlatformLogger.Level.FINE)) {
348            log.fine(xkey.toString());
349        }
350        if (isEventDisabled(xev)) {
351            return;
352        }
353        final Component currentSource = getEventSource();
354        handleKeyPress(xkey);
355    }
356
357}
358