1/*
2 * Copyright (c) 2011, 2012, 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 */
25
26package com.apple.laf;
27
28import java.awt.*;
29import java.awt.event.MouseEvent;
30
31import javax.swing.*;
32import javax.swing.event.*;
33import javax.swing.plaf.ComponentUI;
34import javax.swing.plaf.basic.BasicMenuUI;
35
36public class AquaMenuUI extends BasicMenuUI implements AquaMenuPainter.Client {
37    public static ComponentUI createUI(final JComponent x) {
38        return new AquaMenuUI();
39    }
40
41    protected ChangeListener createChangeListener(final JComponent c) {
42        return new ChangeHandler((JMenu)c, this);
43    }
44
45    protected void installDefaults() {
46        super.installDefaults();
47
48        // [3361625]
49        // In Aqua, the menu delay is 8 ticks, according to Eric Schlegel.
50        // That makes the millisecond delay 8 ticks * 1 second / 60 ticks * 1000 milliseconds/second
51        ((JMenu)menuItem).setDelay(8 * 1000 / 60);
52    }
53
54    protected void paintMenuItem(final Graphics g, final JComponent c, final Icon localCheckIcon, final Icon localArrowIcon, final Color background, final Color foreground, final int localDefaultTextIconGap) {
55        AquaMenuPainter.instance().paintMenuItem(this, g, c, localCheckIcon, localArrowIcon, background, foreground, disabledForeground, selectionForeground, localDefaultTextIconGap, acceleratorFont);
56    }
57
58    protected Dimension getPreferredMenuItemSize(final JComponent c, final Icon localCheckIcon, final Icon localArrowIcon, final int localDefaultTextIconGap) {
59        final Dimension d = AquaMenuPainter.instance().getPreferredMenuItemSize(c, localCheckIcon, localArrowIcon, localDefaultTextIconGap, acceleratorFont);
60        if (c.getParent() instanceof JMenuBar) d.height = Math.max(d.height, 21);
61        return d;
62    }
63
64    public void paintBackground(final Graphics g, final JComponent c, final int menuWidth, final int menuHeight) {
65        final Container parent = c.getParent();
66        final boolean parentIsMenuBar = parent instanceof JMenuBar;
67
68        final ButtonModel model = ((JMenuItem)c).getModel();
69        if (model.isArmed() || model.isSelected()) {
70            if (parentIsMenuBar) {
71                AquaMenuPainter.instance().paintSelectedMenuTitleBackground(g, menuWidth, menuHeight);
72            } else {
73                AquaMenuPainter.instance().paintSelectedMenuItemBackground(g, menuWidth, menuHeight);
74            }
75        } else {
76            if (parentIsMenuBar) {
77                AquaMenuPainter.instance().paintMenuBarBackground(g, menuWidth, menuHeight, c);
78            } else {
79                g.setColor(c.getBackground());
80                g.fillRect(0, 0, menuWidth, menuHeight);
81            }
82        }
83    }
84
85    protected MouseInputListener createMouseInputListener(final JComponent c) {
86        return new AquaMouseInputHandler();
87    }
88
89    protected MenuDragMouseListener createMenuDragMouseListener(final JComponent c) {
90        //return super.createMenuDragMouseListener(c);
91        return new MenuDragMouseHandler();
92    }
93
94    class MenuDragMouseHandler implements MenuDragMouseListener {
95        public void menuDragMouseDragged(final MenuDragMouseEvent e) {
96            if (menuItem.isEnabled() == false) return;
97
98            final MenuSelectionManager manager = e.getMenuSelectionManager();
99            final MenuElement path[] = e.getPath();
100
101            // In Aqua, we always respect the menu's delay, if one is set.
102            // Doesn't matter how the menu is clicked on or otherwise moused over.
103            final Point p = e.getPoint();
104            if (p.x >= 0 && p.x < menuItem.getWidth() && p.y >= 0 && p.y < menuItem.getHeight()) {
105                final JMenu menu = (JMenu)menuItem;
106                final MenuElement selectedPath[] = manager.getSelectedPath();
107                if (!(selectedPath.length > 0 && selectedPath[selectedPath.length - 1] == menu.getPopupMenu())) {
108                    if (menu.getDelay() == 0) {
109                        appendPath(path, menu.getPopupMenu());
110                    } else {
111                        manager.setSelectedPath(path);
112                        setupPostTimer(menu);
113                    }
114                }
115            } else if (e.getID() == MouseEvent.MOUSE_RELEASED) {
116                final Component comp = manager.componentForPoint(e.getComponent(), e.getPoint());
117                if (comp == null) manager.clearSelectedPath();
118            }
119        }
120
121        public void menuDragMouseEntered(final MenuDragMouseEvent e) { }
122        public void menuDragMouseExited(final MenuDragMouseEvent e) { }
123        public void menuDragMouseReleased(final MenuDragMouseEvent e) { }
124    }
125
126    static void appendPath(final MenuElement[] path, final MenuElement elem) {
127        final MenuElement newPath[] = new MenuElement[path.length + 1];
128        System.arraycopy(path, 0, newPath, 0, path.length);
129        newPath[path.length] = elem;
130        MenuSelectionManager.defaultManager().setSelectedPath(newPath);
131    }
132
133    protected class AquaMouseInputHandler extends MouseInputHandler {
134        /**
135         * Invoked when the cursor enters the menu. This method sets the selected
136         * path for the MenuSelectionManager and handles the case
137         * in which a menu item is used to pop up an additional menu, as in a
138         * hierarchical menu system.
139         *
140         * @param e the mouse event; not used
141         */
142        public void mouseEntered(final MouseEvent e) {
143            final JMenu menu = (JMenu)menuItem;
144            if (!menu.isEnabled()) return;
145
146            final MenuSelectionManager manager = MenuSelectionManager.defaultManager();
147            final MenuElement selectedPath[] = manager.getSelectedPath();
148
149            // In Aqua, we always have a menu delay, regardless of where the menu is.
150            if (!(selectedPath.length > 0 && selectedPath[selectedPath.length - 1] == menu.getPopupMenu())) {
151                // the condition below prevents from activating menu in other frame
152                if (!menu.isTopLevelMenu() || (selectedPath.length > 0 &&
153                        selectedPath[0] == menu.getParent())) {
154                    if (menu.getDelay() == 0) {
155                        appendPath(getPath(), menu.getPopupMenu());
156                    } else {
157                        manager.setSelectedPath(getPath());
158                        setupPostTimer(menu);
159                    }
160                }
161            }
162        }
163    }
164}
165