1/*
2 * Copyright (c) 1997, 2007, 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.sun.java.swing.plaf.windows;
27
28import java.awt.*;
29import java.awt.event.MouseEvent;
30
31import javax.swing.plaf.ComponentUI;
32import javax.swing.plaf.basic.BasicMenuUI;
33import javax.swing.event.MouseInputListener;
34import javax.swing.*;
35
36import com.sun.java.swing.plaf.windows.TMSchema.Part;
37import com.sun.java.swing.plaf.windows.TMSchema.State;
38
39/**
40 * Windows rendition of the component.
41 * <p>
42 * <strong>Warning:</strong>
43 * Serialized objects of this class will not be compatible with
44 * future Swing releases.  The current serialization support is appropriate
45 * for short term storage or RMI between applications running the same
46 * version of Swing.  A future release of Swing will provide support for
47 * long term persistence.
48 */
49public class WindowsMenuUI extends BasicMenuUI {
50    protected Integer menuBarHeight;
51    protected boolean hotTrackingOn;
52
53    final WindowsMenuItemUIAccessor accessor =
54        new WindowsMenuItemUIAccessor() {
55
56            public JMenuItem getMenuItem() {
57                return menuItem;
58            }
59
60            public State getState(JMenuItem menu) {
61                State state = menu.isEnabled() ? State.NORMAL
62                        : State.DISABLED;
63                ButtonModel model = menu.getModel();
64                if (model.isArmed() || model.isSelected()) {
65                    state = (menu.isEnabled()) ? State.PUSHED
66                            : State.DISABLEDPUSHED;
67                } else if (model.isRollover()
68                           && ((JMenu) menu).isTopLevelMenu()) {
69                    /*
70                     * Only paint rollover if no other menu on menubar is
71                     * selected
72                     */
73                    State stateTmp = state;
74                    state = (menu.isEnabled()) ? State.HOT
75                            : State.DISABLEDHOT;
76                    for (MenuElement menuElement :
77                        ((JMenuBar) menu.getParent()).getSubElements()) {
78                        if (((JMenuItem) menuElement).isSelected()) {
79                            state = stateTmp;
80                            break;
81                        }
82                    }
83                }
84
85                //non top level menus have HOT state instead of PUSHED
86                if (!((JMenu) menu).isTopLevelMenu()) {
87                    if (state == State.PUSHED) {
88                        state = State.HOT;
89                    } else if (state == State.DISABLEDPUSHED) {
90                        state = State.DISABLEDHOT;
91                    }
92                }
93
94                /*
95                 * on Vista top level menu for non active frame looks disabled
96                 */
97                if (((JMenu) menu).isTopLevelMenu() && WindowsMenuItemUI.isVistaPainting()) {
98                    if (! WindowsMenuBarUI.isActive(menu)) {
99                        state = State.DISABLED;
100                    }
101                }
102                return state;
103            }
104
105            public Part getPart(JMenuItem menuItem) {
106                return ((JMenu) menuItem).isTopLevelMenu() ? Part.MP_BARITEM
107                        : Part.MP_POPUPITEM;
108            }
109    };
110    public static ComponentUI createUI(JComponent x) {
111        return new WindowsMenuUI();
112    }
113
114    protected void installDefaults() {
115        super.installDefaults();
116        if (!WindowsLookAndFeel.isClassicWindows()) {
117            menuItem.setRolloverEnabled(true);
118        }
119
120        menuBarHeight = (Integer)UIManager.getInt("MenuBar.height");
121
122        Object obj      = UIManager.get("MenuBar.rolloverEnabled");
123        hotTrackingOn = (obj instanceof Boolean) ? (Boolean)obj : true;
124    }
125
126    /**
127     * Draws the background of the menu.
128     * @since 1.4
129     */
130    protected void paintBackground(Graphics g, JMenuItem menuItem, Color bgColor) {
131        if (WindowsMenuItemUI.isVistaPainting()) {
132            WindowsMenuItemUI.paintBackground(accessor, g, menuItem, bgColor);
133            return;
134        }
135
136        JMenu menu = (JMenu)menuItem;
137        ButtonModel model = menu.getModel();
138
139        // Use superclass method for the old Windows LAF,
140        // for submenus, and for XP toplevel if selected or pressed
141        if (WindowsLookAndFeel.isClassicWindows() ||
142            !menu.isTopLevelMenu() ||
143            (XPStyle.getXP() != null && (model.isArmed() || model.isSelected()))) {
144
145            super.paintBackground(g, menu, bgColor);
146            return;
147        }
148
149        Color oldColor = g.getColor();
150        int menuWidth = menu.getWidth();
151        int menuHeight = menu.getHeight();
152
153        UIDefaults table = UIManager.getLookAndFeelDefaults();
154        Color highlight = table.getColor("controlLtHighlight");
155        Color shadow = table.getColor("controlShadow");
156
157        g.setColor(menu.getBackground());
158        g.fillRect(0,0, menuWidth, menuHeight);
159
160        if (menu.isOpaque()) {
161            if (model.isArmed() || model.isSelected()) {
162                // Draw a lowered bevel border
163                g.setColor(shadow);
164                g.drawLine(0,0, menuWidth - 1,0);
165                g.drawLine(0,0, 0,menuHeight - 2);
166
167                g.setColor(highlight);
168                g.drawLine(menuWidth - 1,0, menuWidth - 1,menuHeight - 2);
169                g.drawLine(0,menuHeight - 2, menuWidth - 1,menuHeight - 2);
170            } else if (model.isRollover() && model.isEnabled()) {
171                // Only paint rollover if no other menu on menubar is selected
172                boolean otherMenuSelected = false;
173                MenuElement[] menus = ((JMenuBar)menu.getParent()).getSubElements();
174                for (int i = 0; i < menus.length; i++) {
175                    if (((JMenuItem)menus[i]).isSelected()) {
176                        otherMenuSelected = true;
177                        break;
178                    }
179                }
180                if (!otherMenuSelected) {
181                    if (XPStyle.getXP() != null) {
182                        g.setColor(selectionBackground); // Uses protected field.
183                        g.fillRect(0, 0, menuWidth, menuHeight);
184                    } else {
185                        // Draw a raised bevel border
186                        g.setColor(highlight);
187                        g.drawLine(0,0, menuWidth - 1,0);
188                        g.drawLine(0,0, 0,menuHeight - 2);
189
190                        g.setColor(shadow);
191                        g.drawLine(menuWidth - 1,0, menuWidth - 1,menuHeight - 2);
192                        g.drawLine(0,menuHeight - 2, menuWidth - 1,menuHeight - 2);
193                    }
194                }
195            }
196        }
197        g.setColor(oldColor);
198    }
199
200    /**
201     * Method which renders the text of the current menu item.
202     *
203     * @param g Graphics context
204     * @param menuItem Current menu item to render
205     * @param textRect Bounding rectangle to render the text.
206     * @param text String to render
207     * @since 1.4
208     */
209    protected void paintText(Graphics g, JMenuItem menuItem,
210                             Rectangle textRect, String text) {
211        if (WindowsMenuItemUI.isVistaPainting()) {
212            WindowsMenuItemUI.paintText(accessor, g, menuItem, textRect, text);
213            return;
214        }
215        JMenu menu = (JMenu)menuItem;
216        ButtonModel model = menuItem.getModel();
217        Color oldColor = g.getColor();
218
219        // Only paint rollover if no other menu on menubar is selected
220        boolean paintRollover = model.isRollover();
221        if (paintRollover && menu.isTopLevelMenu()) {
222            MenuElement[] menus = ((JMenuBar)menu.getParent()).getSubElements();
223            for (int i = 0; i < menus.length; i++) {
224                if (((JMenuItem)menus[i]).isSelected()) {
225                    paintRollover = false;
226                    break;
227                }
228            }
229        }
230
231        if ((model.isSelected() && (WindowsLookAndFeel.isClassicWindows() ||
232                                    !menu.isTopLevelMenu())) ||
233            (XPStyle.getXP() != null && (paintRollover ||
234                                         model.isArmed() ||
235                                         model.isSelected()))) {
236            g.setColor(selectionForeground); // Uses protected field.
237        }
238
239        WindowsGraphicsUtils.paintText(g, menuItem, textRect, text, 0);
240
241        g.setColor(oldColor);
242    }
243
244    protected MouseInputListener createMouseInputListener(JComponent c) {
245        return new WindowsMouseInputHandler();
246    }
247
248    /**
249     * This class implements a mouse handler that sets the rollover flag to
250     * true when the mouse enters the menu and false when it exits.
251     * @since 1.4
252     */
253    protected class WindowsMouseInputHandler extends BasicMenuUI.MouseInputHandler {
254        public void mouseEntered(MouseEvent evt) {
255            super.mouseEntered(evt);
256
257            JMenu menu = (JMenu)evt.getSource();
258            if (hotTrackingOn && menu.isTopLevelMenu() && menu.isRolloverEnabled()) {
259                menu.getModel().setRollover(true);
260                menuItem.repaint();
261            }
262        }
263
264        public void mouseExited(MouseEvent evt) {
265            super.mouseExited(evt);
266
267            JMenu menu = (JMenu)evt.getSource();
268            ButtonModel model = menu.getModel();
269            if (menu.isRolloverEnabled()) {
270                model.setRollover(false);
271                menuItem.repaint();
272            }
273        }
274    }
275
276    protected Dimension getPreferredMenuItemSize(JComponent c,
277                                                     Icon checkIcon,
278                                                     Icon arrowIcon,
279                                                     int defaultTextIconGap) {
280
281        Dimension d = super.getPreferredMenuItemSize(c, checkIcon, arrowIcon,
282                                                     defaultTextIconGap);
283
284        // Note: When toolbar containers (rebars) are implemented, only do
285        // this if the JMenuBar is not in a rebar (i.e. ignore the desktop
286        // property win.menu.height if in a rebar.)
287        if (c instanceof JMenu && ((JMenu)c).isTopLevelMenu() &&
288            menuBarHeight != null && d.height < menuBarHeight) {
289
290            d.height = menuBarHeight;
291        }
292
293        return d;
294    }
295}
296